Java中的wait/notify/notifyAll

方法

java.lang.Objectjava

public final native void wait() throws InterruptedException;
public final native void wait(long millis, int nanos) throws InterruptedException;
public final void wait(long millis) throws InterruptedException {
    wait(millis, 0);
}

public final native void notify();
public final native void notifyAll();

wait():使调用该方法的线程释放锁,从运行状态退出,进入等待队列,直到被唤醒。spa

wait(long timeout):等待一段时间是否有线程唤醒锁,若是没有,超时自动唤醒。线程

wait(long timeout, int nanos):等待唤醒时间纳秒级别。code

notify():随机唤醒等待队列中的等待同一个锁的一个线程,使这个线程退出等待队列,进入可运行状态。对象

notifyAll():唤醒全部等待一样锁的全部线程,从等待队列中退出,进入可运行状态。队列

注意点

  1. 在调用wait或者notify以前,必须得到该对象的对象锁,即,只能在同步方法中调用;
  2. 执行完wait以后释放对象锁,因此其余线程能够得到执行机会,才能唤醒;
  3. 执行notify以后,不会当即退出让wait的线程执行,必需要先把同步块中的程序执行完,退出同步块,才会释放锁,让等待线程执行;
  4. notify每次通知一个线程,屡次调用通知线程数增长,可将wait线程所有唤醒。

原理

每一个对象都有个monitor,初始是0,执行完synchronized值就是1。ip

wait/notify须要在得到monitor的线程中才能够执行。资源

因此,wait/notify须要在synchronized中执行同步

其中,wait又会释放掉锁,破坏掉同步。it

跟synchronized关系

synchronized代码块生成的字节码,被monitorentermonitorexit包围,持有对象的monitor;

线程执行wait/notify方法时,必须持有对象的monitor;

因此,wait/notify方法在synchronized同步块中执行,就持有了对象的锁。

互斥和协同

Java语言的同步机制在底层实现上只有两种方式:互斥和协同。

互斥:即synchronized内置锁。

协同:即内置条件队列,wait/notify/notifyAll。

条件队列中是处于等待状态的线程,等待特定条件为真。每一个Java对象均可以做为一个锁,一样每一个Java对象均可以做为一个条件队列。经过wait/notify/notifyAll来操做条件队列。

能够理解为:有一个队列,o.wait()就push进去,o.notify()就pull出来。

要调用条件队列的任何一个方法,都必需要得到对象上的锁。

线程是用来工做的,不该该处于等待状态,处于等待状态的条件队列中的线程,必定是执行不下去的。

在while中等待

while(condition is not true) {
  lock.wait()
}

解释:两个消费者线程c1和c2,逻辑都是,判断资源是否为空,是就wait,否就消费一个;某个时刻,两个线程都进入等待队列,而后生产者生产了一个资源,并执行notifyAll,唤醒c1和c2都进入锁池,c1先获取锁,执行完消费掉资源,而后释放锁,此时,若是c2得到锁,若是是if逻辑,那么就会进入消费代码,可是资源已经被c1消费掉了,可能抛出异常。若是是while逻辑,则不会进入消费代码,而是继续等待。

在通常状况下,总应该调用notifyAll唤醒全部须要被唤醒的线程。可能会唤醒其余一些线程,但这不影响程序的正确性,这些线程醒来以后,会检查他们正在等待的条件(循环检测),若是发现条件不知足,就会继续等待

clipboard.png

显示锁和显示条件队列

显示锁:Lock,对应内置锁synchronized

显示条件队列:Condition,对应内置条件队列,对应方法是await, signal, signalAll

问题

  1. notifyAll唤醒全部线程,但不是全部线程都能执行,必需要等待对象锁被释放,获取锁以后才能执行。能够说,notifyAll让线程进入锁池。
相关文章
相关标签/搜索