问题
我们都知道,为了调用Object.wait()
,这个调用必须放在synchronized块中,否则抛出anIllegalMonitorStateException
。但**这个限制的原因是什么?**我知道wait()
释放了监视器,但为什么我们需要通过使特定块同步来显式获取监视器,然后通过调用wait()
释放监视器?
如果有可能在同步块中调用wait()
,保留它的语义 - 暂停调用程序线程,可能造成的损害是什么?
#1 热门回答(243 赞)
如果可以在同步块外部调用wait(),保留它的语义 - 暂停调用程序线程,那么可能造成的损害是什么?
让我们举例说明ifwait()
可以在带有具体示例的同步块之外调用的问题。
假设我们要实现一个阻塞队列(我知道,API中已有一个:)
第一次尝试(没有同步)可能看起来像下面的行
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();
}
}
这是可能发生的事情:
- 消费者线程调用take()并看到buffer.isEmpty()。
- 在消费者线程继续调用wait()之前,生成器线程出现并调用完整的give(),即buffer.add(data);通知();
- 消费者线程现在将调用wait()(并且错过刚刚调用的notify())。
- 如果运气不好,生产者线程不会产生更多的give(),因为消费者线程永远不会醒来,而且我们有一个死锁。
一旦你理解了这个问题,解决方案是显而易见的:始终执行58233163/notify
和isEmpty
/wait
。
没有详细说明:此同步问题是通用的。正如Michael Borgwardt所指出的那样,wait / notify是关于线程之间的通信的,所以你总是会遇到类似于上面描述的竞争条件。这就是强制执行"仅在内部同步"规则的原因。
link posted by @Willie中的一段很好地说明了这一点:
你需要绝对保证服务员和通知者同意谓词的状态。服务员在进入睡眠状态之前稍微检查一下谓词的状态,但这取决于谓词在进入睡眠状态时的正确性。这两个事件之间存在一段时间的脆弱性,这可能会破坏程序。
生产者和消费者需要达成一致的谓词在上面的例子中:buffer.isEmpty()
。并且通过确保在synchronized
块中执行等待和通知来解决协议。
这篇文章已被重写为文章:Java: Why wait must be called in a synchronized block
#2 热门回答(194 赞)
Await()
在有anotify()
时才有意义,所以它始终是线程之间的通信,需要同步才能正常工作。有人可能会认为这应该是隐含的,但这并不会有所帮助,原因如下:
在语义上,你永远不会只是wait()
。你需要一些条件才能得到满足,如果不是,你就要等到它。所以你真正做的是
if(!condition){
wait();
}
但是条件是由一个单独的线程设置的,所以为了使这个工作正常,你需要同步。
还有一些问题,只是因为你的线程退出等待并不意味着你正在寻找的条件是真的:
- 你可以得到虚假的唤醒(意味着一个线程可以在没有收到通知的情况下从等待中醒来),或者
- 条件可以设置,但是第三个线程在等待线程唤醒(并重新获取监视器)时再次使条件为false。
为了处理这些情况你真正需要的是以下一些变化:
synchronized(lock){
while(!condition){
lock.wait();
}
}
更好的是,不要混淆同步原语并使用java.util.concurrent
包中提供的抽象。
#3 热门回答(10 赞)
@Rollerball是对的。调用了wait()
,这样线程就可以等到发生这种情况时发生某种情况,线程被迫放弃锁定。
要放弃一些东西,你需要先拥有它。线程首先需要拥有锁。因此需要在asynchronized
方法/块中调用它。
是的,如果你未在synchronized
方法/区块内检查条件,我同意上述有关潜在损害/不一致的所有答案。但是正如@ shrini1000指出的那样,只需在同步块中调用wait()
就不会避免这种不一致的发生。
Here is a nice read..