Java中wait()方法为什么要放在同步块中 即lost wake up问题

阿里面试题,Java中wait()方法为什么要放在同步块中?

lost wake up 问题:https://www.jianshu.com/p/b8073a6ce1c0

问:Java 多线程中 wait() 方法为什么要放在同步块中?

答:为了避免「lost wake up 问题」,即「无法唤醒问题」。

什么是「lost wake up 问题」

我对「lost wake up 问题」的通俗理解:线程 A 调用 wait() 方法进入阻塞状态,接下来没有其他线程去唤醒线程 A,或者其他线程唤醒时机不对(早于线程 A 的 wait() ),导致线程 A 永远阻塞下去。

有中文资料对这个问题作出过解释:Java中wait()方法为什么要放在同步块中?(lost wake-up 问题),文中举了生产者和消费者的例子,但我觉得此文的结论并未触达核心,需要对文中的例子和结论多做一下补充,理解起来会方便一点。

现在有一个生产者线程和消费者线程:

先定义一个 obj 对象,并将其 count 属性的初始值设置为 0:

Object obj = new Object();

obj.count = 0;

生产者伪代码:

obj.count++;

obj.notify();

消费者伪代码:

while(obj.count<=0)

obj.wait();

obj.count--;

两个线程启动,消费者检查 obj.count 的值,发现 obj.count <= 0 条件成立,但这时由于 CPU 的调度,发生上下文切换,生产者开始工作,执行了 count+1 和 obj.notify(),也就是发出通知,准备唤醒一个阻塞的线程。然后 CPU 调度到消费者,此时消费者开始执行 obj.wait(),线程进入阻塞。但生产者已经早在消费者阻塞前执行了唤醒动作,也就导致消费者永远无法醒来了。

随便加个锁能解决「lost wake up 问题」吗?

不能,举个例子。

定义一把锁:

Lock lock1 = new Lock();

生产者伪代码:

lock1.lock();

obj.count++;

obj.notify();

lock1.unlock();

消费者伪代码:

lock1.lock();

while(count<=0)

obj.wait();

obj.count--;

lock1.unlock();

两个线程启动,obj.count 初始值为 0。假设消费者先竞争到锁,while 中的 obj.count<=0 条件满足,执行 obj.wait() 使线程进入阻塞状态,lock1 锁没有被释放,所以生产者拿不到锁,也就无法 obj.notify() 通知消费者醒来,消费者将永远阻塞下去。

Java 中什么锁才能解决「lost wake up 问题」

只有上述例子中的 obj 对象锁才能避免这个问题,也就是将 obj.wait() 和 obj.notify() 放进 obj 对象锁的同步块中。如果锁的不是例子中的 obj 对象,Java 就会抛出 IllegalMonitorStateException 异常。

生产者伪代码:

synchronized (obj) {
obj.count++;

obj.notify();

}

消费者伪代码:

synchronized (obj) {
while(count<=0)

obj.wait();

obj.count--;

}

Java 中对 wait() 方法的注释中提到:线程在调用 obj.wait() 前必须要拿到当前 obj 对象的监视器 monitor 对象,即 obj 的锁。只有这样,当执行到 obj.wait() 时,该线程才可以暂时让出 obj 的同步锁并停止对锁的竞争,让其他正在等待此锁的线程可以得到同步锁并运行。

在上述例子中,消费者执行到 obj.wait() 时,让出了 obj 锁,停止了对锁的竞争,进入阻塞状态,紧接着生产者竞争到 obj 锁,执行了 obj.notify() 方法,唤醒了消费者,使消费者线程从阻塞状态重新回到就绪状态。

这里要注意的是,obj.notify() 并不是让生产者马上释放锁,也不是让消费者马上得到锁,而是通知消费者线程可以重新去参与锁的竞争了。
————————————————
版权声明:本文为CSDN博主「米死胃生」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_33764387/article/details/114593958

上一篇:uniq -c 去掉重复行


下一篇:墨天轮Oracle免费在线Docker学习云主机