wait是多个线程间进行交互的命令,调用wait后会挂起线程,等待别的线程调用notify/notify all命令唤醒
而开发中,经常会先判断某种状态,如果不符合便调用wait()挂起线程等待别的线程唤醒。但是在状态判定和实际挂起这两个事件之间的时间是十分脆弱的。无法保证在实际挂起线程时,判断状态仍然满足。
假设要开发一个线程安全的队列,代码如下所示:
class BlockingQueue {
Queue<String> buffer = new LinkedList<String>();
public void give(String data) {
buffer.add(data);
notify(); // Since someone may be waiting in take!
}
public String take() throws InterruptedException {
while (buffer.isEmpty()) // don't use "if" due to spurious wakeups.
wait();
return buffer.remove();
}
}
上面的队列可能存在以下潜在问题:
-
消费者A调用
take()
时buffer.isEmpty()
为true -
在消费者A调用
wait()
方法之前, 生产者B调用了give()
, (buffer.add(data); notify();
)
-
之后消费者A调用了
wait()
(错过了生产者B调用的notify()). -
give()如果之后没有别的生产者调用give()方法,消费者A所在线程则会一直等待。
解决方法如下: 总是保证 give
/notify
和 isEmpty
/wait
操作的原子性。这也是为什么wait方法要强制在同步代码块中调用.
问题地址:http://stackoverflow.com/questions/2779484/why-must-wait-always-be-in-synchronized-block