Java的线程基础知识

103 阅读3分钟

一、线程间通信的两种方式

1、wait()/notify()

Object类中相关的方法有notify()、notifyAll()和wait()。因为这些方法是定义在Object类中,因此会被所有的类所继承。这些方法都是final修饰,因而不能被重写


  • wait()方法:让当前进程进入等待状态,并释放锁,同时释放CPU
  • wait(long)方法:让当前线程进入等待,并释放锁,不过等待时间为long超过这个时间没有对当前线程进行唤醒,将自动唤醒
  • notify()方法:让当前线程通知处于等待状态的线程,当前线程完毕后释放锁,并从其他线程中唤醒其中一个继续执行。
  • notifyAll()方法:让当前线程通知处于等待状态的线程,当前线程执行完毕后释放锁,将唤醒所有等待状态的线程。


wait()方法使用注意事项:

  • 当前线程拥有当前对象的锁时,才能调用wait()方法,否则将抛出异常java.lang.IllegalMonitorStateException
  • 线程调用wait()方法,释放它对锁的使用权,并且等待其他线程通知它(notify()或notifyAll()),这样它才能重新获得锁的使用权和恢复执行。
  • 要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。


wait()、sleep()、yield()、join()比较

  • 线程调用wait()方法时,它会释放对象的锁,并释放CPU,线程放入等待队列中;
  • sleep()方法不释放锁,但释放CPU,线程被放入超时等待队列中;
  • yield()方法仅释放CPU,锁占用,线程会别放入就绪对列中;
  • join()方法释放CPU,也释放锁(join()源码中使用wait()方法),线程被放入超时等待队列中。(join()方法只能管到它所在位置后面的线程。即,join()方法所在位置后面的线程才会一直等待调用该方法的线程执行完,在它前面的不会)。


线程状态:

有资源有CPU执行权:运行态

有资源无CPU执行权:就绪态

无资源无执行器:阻塞态(会一直请求资源)

主动释放资源和执行权,不设置超时时间,需要被唤醒:等待态

主动释放资源和执行权,设置超时时间,能自动唤醒:超时等待态


2、Condition实现等待/通知

关键字synchronized与wait()和notify()/notifyAll()方法相结合可以实现等待/通知模式,类似Reentrantlock也可以实现同样的功能,但需要借助于Condition对象。


关于Condition实现等待/通知可以类比wait()/notify(),如下:

condition.await() ——————> lock.wait()
condition.await(long time, TimeUnit unit)  ——————> lock.wait(long timeout)
condition.signal()  ——————> lock.notify()
condition.signalAll()  ——————> lock.notifyAll()


特殊之处:

synchronized相当于整个ReentrantLock对象只有一个单一的Condition对象情况。

而一个ReentrantLock却可以拥有多个Condition对象,来实现通知部分线程。

具体实现方式:
假设有两个Condition对象:ConditionA和ConditionB。那么由ConditionA.await()方法进入等待状态的线程,由ConditionA.signalAll()通知唤醒;由ConditionB.await()方法进入等待状态的线程,由ConditionB.signalAll()通知唤醒。