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

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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值