wait、notify和notifyAll三个方法必须放在同步块内执行。
现象:
如果不放在同步块内,运行时会抛出这个异常:
IllegalMonitorStateException。
原因:
因为wait和notify(包括notifyAll,为了方便,后面仅写notify)的使用一般而言,都是成对的(自己等待,通知别人)。
举一个生产者消费者的例子,大概是下面这样的模式:
生产者producer:
if full
wait;
else
produce;
notify consumer;
消费者consumer:
if empty
wait
else
do consume;
notify producer;
如果消费者或者生产者的逻辑不放在同步块内,那么整个生产或者消费就不是原子性的操作,那么可能会有这样的问题:
比如说生产者先判断full,那么准备执行wait,但是此时线程切换到了消费者,消费者执行消费,并且notify。随后又切换到了生产者线程,此时由于消费者消费了,那么就不是full了,生产者本应该生产,但是却继续执行了wait,等于说之前的一次notify丢失了。
回顾一下这个过程,本质上是因为wait和notify的操作不是同步执行导致的。所以,wait和notify执行前必须拿到锁才行,这就是需要放到同步块内执行的原因。
最后,打开wait方法的源码:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait();
* ... // Perform action appropriate to condition
* }
* </pre>
作者建议wait的调用应该始终用如上形式:
1.同步;
2.while判断条件,而不是if;
同步的原因上面已经解释过了。为什么要用while?
这是因为,如果线程数不止一个,比如消费者和生产者都有多个线程,那么notify后,只有一个线程可以执行,其余的线程仍然需要wait,此时就必须用while保证可以重新while。