虚假唤醒 Spurious wakeup
如果等待线程在没有通知被调用的情况下唤醒,则称为Spurious wakeup。
解决方案就是:
使用while条件判断,更好的方案是避免使用wait这种低级的API,而是使用高级的并发工具。
因为这些高级的并发工具都是经过无数的坑才提炼出来的,如果你对底层缺乏深入的了解比如不知道虚假唤醒那么你没有做这种处理,你的代码可能会出问题。
synchronized(obj){
while(<condition not hold>)
obj.wait();
... //执行适合条件的操作
}
}
这是使用wait()方法的标准习惯用法。在上面的场景中,如果任何其他线程发送了notify(),那么条件将不会成立并且将跳过wait()。考虑在此线程调用wait()之前是否没有while循环和其他一些线程调用通知,然后可能会发生它可能永远等待或直到调用下一个notify。
JDK 5中的等待方法的javadoc也已更新
线程也可以在没有被通知,中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防范它,并且如果条件不满足则继续等待。换句话说,等待应始终在循环中进行
Src:有效的Java作者:Joshua Bloch https://tech-read.com/2010/01/28/spurious-wakeup-in-java/
https://en.m.wikipedia.org/wiki/Spurious_wakeup
http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html
==================================
看看其他人对Doug Lea关于虚假唤醒的评论:
https://coderanch.com/t/234023/java/spurious-wakeup
=============
http://opensourceforgeeks.blogspot.com/2014/08/spurious-wakeups-in-java-and-how-to.html
==============
=====================
=================================
问题代码分析:
https://www.linuxidc.com/Linux/2014-03/98715.htm
如何修复问题?
#1. 使用可同步的数据结构来存放数据,比如LinkedBlockingQueue之类。由这些同步的数据结构来完成繁琐的同步操作。
#2. 双层的synchronized使用没有意义,保留外层即可。
#3. 将if替换为while,解决虚假唤醒的问题。