1 为什么wait/notify必须要强制要求放在synchronized中

在日常开发,咱们都晓得wait/notify有着十分固定的一套模板,就是上面的样子,synchronized同步块包裹着Object.wait()办法,如果不通过同步块包住的话JVM会抛出IllegalMonitorStateException异样。

synchronized(lock) {    while(!condition){        lock.wait();    }}

那么为什么要限度这么写呢?

2 如果Object.wait()/notify不须要同步

假如咱们本人实现了一个BlockingQueue的代码。
如果Object.wait()/notify不须要同步,那么咱们的代码会形如上面这样。

class BlockingQueue {    Queue<String> buffer = new LinkedList<String>();    public void give(String data) {        buffer.add(data);        notify();                   // 往队列里增加的时候notify,因为可能有人在等着take    }    public String take() throws InterruptedException {        while (buffer.isEmpty())    // 用while,避免spurious wakeups(虚伪唤醒)            wait(); // 当buffer是空的时候就等着他人give        return buffer.remove();    }}

如果下面的代码能够执行的话,多线程状况下会呈现一种状况:

  1. 以后buffer是空的,这时来了一个take的申请,尚未执行到wait语句
  2. 同时又来了一个give申请,残缺执行完了整个give办法并且发送了notify
  3. 此时take办法才走到wait,因为它错过了上一个notify,所以会在明明buffer不空的状况下挂起线程,take办法挂起。如果再没有人调用过give办法了,在业务上的体现就会是这个take线程永远也取不到buffer中的内容

3 为什么要在JVM层面抛异样

因为你只有用notify,那就是为了在多线程环境下同步,notify/wait机制自身就是为了多线程的同步而存在的,那就只能配套synchronized,所以为了避免下面状况的产生,就间接强制抛异样来限度开发的代码模式了。

4 如果没有wait/notify

试想一种场景,如果没有wait/notify的挂起唤醒机制,该如何实现BlockingQueue

class BlockingQueue {    Queue<String> buffer = new LinkedList<String>();    public void give(String data) {        buffer.add(data);    }    public String take() throws InterruptedException {        while(buffer.isEmpty) {            sleep(10);        }        return buffer.remove();    }}

如果没有wait/notify,那么在take的时候只能通过while循环不停轮询判断buffer是否为空来实时获取buffer的最新状态,那么势必会造成两种状况:

  1. sleep工夫过短,那么线程将始终不行循环抢占CPU,造成CPU打满
  2. sleep工夫过长,那么将会影响take的时效性

综上,针对于BlockingQueue这样的场景,同步块 + wait/notify 或者 lock + signal/await 就是标配