为何wait/notify必需要强制要求放在synchronized中

1 为何wait/notify必需要强制要求放在synchronized中

在平常开发,咱们都知道wait/notify有着很是固定的一套模板,就是下面的样子,synchronized同步块包裹着Object.wait()方法,若是不经过同步块包住的话JVM会抛出IllegalMonitorStateException异常。java

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

那么为何要限制这么写呢?多线程

2 若是Object.wait()/notify不须要同步

假设咱们本身实现了一个BlockingQueue的代码。
若是Object.wait()/notify不须要同步,那么咱们的代码会形以下面这样。spa

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中的内容

image.png

3 为何要在JVM层面抛异常

由于你只要用notify,那就是为了在多线程环境下同步,notify/wait机制自己就是为了多线程的同步而存在的,那就只能配套synchronized,因此为了防止上面状况的发生,就直接强制抛异常来限制开发的代码模式了。code

4 若是没有wait/notify

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

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 就是标配开发

相关文章
相关标签/搜索