Java中wait()方法为什么要放在同步块中?(lost wake-up 问题):
https://www.cnblogs.com/sunweiye/p/11055550.html
lost wakeup问题
sleep 和 wakeup使用于等待某事发生,用于阻塞和唤醒线程的情形中。
所以sleep的使用逻辑是:检查状态,不符合则调用sleep阻塞当前线程;wakeup的使用逻辑是:改变状态,调用wakeup唤醒对应线程。
lost wakeup问题:(1)线程1首先检查状态,发现不符合。(2)线程2改变状态,调用wakeup方法。但是这时线程1还未调用sleep方法,其还未被阻塞,所以wakeup丢失。(3)最后线程1调用sleep方法,阻塞线程,且无法再被唤醒。
解决方案
-
sleep 和 wakeup必须位于同一同步块中,且保证sleep方法保证释放锁和阻塞线程的原子性。线程1和线程2在检查/改变状态之前都要先获取锁,sleep方法调用时释放锁,保证线程2无法在线程1检查状态之后,调用sleep方法之前改变状态,并调用wakeup。
-
sleep方法保证释放锁和阻塞线程的原子性在应用层面很难实现,所以JAVA AQS中先释放锁,再阻塞线程,所以还是会有lost wakeup问题。但是如果wakeup唤醒的对应线程还未被阻塞的话(即释放锁之后,阻塞线程之前),会重复调用wakeup函数,直到对应线程被阻塞,然后对其唤醒。