我们都知道为了调用Object.wait(),这个调用必须放在同步块中,否则抛出IllegalMonitorStateException。但是为什么要做出这样的限制呢?我知道wait()释放监视器,但为什么我们需要通过使特定块同步显式获取监视器,然后通过调用wait()释放监视器?
如果可以在同步块之外调用wait(),保留它的语义——挂起调用者线程,那么潜在的损害是什么?
我们都知道为了调用Object.wait(),这个调用必须放在同步块中,否则抛出IllegalMonitorStateException。但是为什么要做出这样的限制呢?我知道wait()释放监视器,但为什么我们需要通过使特定块同步显式获取监视器,然后通过调用wait()释放监视器?
如果可以在同步块之外调用wait(),保留它的语义——挂起调用者线程,那么潜在的损害是什么?
当前回答
直接从这个Java oracle教程:
当线程调用d.wait时,它必须拥有d -的内在锁 否则抛出错误。在synchronized中调用等待 方法是一种获取内在锁的简单方法。
其他回答
当您在对象t上调用notify()时,Java会通知特定的t.wait()方法。但是,Java如何搜索并通知特定的等待方法呢?
Java只查看被对象t锁定的同步代码块。Java不能搜索整个代码来通知特定的t.wait()。
这基本上与硬件架构(即RAM和缓存)有关。
如果你没有将synchronized与wait()或notify()一起使用,另一个线程可能会进入同一个块,而不是等待监视器进入它。此外,当访问一个没有同步块的数组时,另一个线程可能看不到它的变化…实际上,当另一个线程已经在处理CPU内核的x级缓存(也就是1 /2 /3级缓存)中有一个数组的副本时,它不会看到对它的任何更改。
但是同步块只是奖章的一面:如果您实际上从非同步上下文中访问同步上下文中的对象,即使在同步块中,该对象仍然不会被同步,因为它在其缓存中保存了对象的自己副本。我在这里写了这个问题:https://stackoverflow.com/a/21462631和当一个锁持有一个非final对象时,对象的引用仍然可以被另一个线程改变吗?
此外,我确信x级缓存要对大多数不可重现的运行时错误负责。这是因为开发人员通常不了解底层的东西,比如CPU如何工作或内存层次结构如何影响应用程序的运行:http://en.wikipedia.org/wiki/Memory_hierarchy
为什么编程类不首先从内存层次结构和CPU架构开始,这仍然是一个谜。“Hello world”在这里没用。;)
直接从这个Java oracle教程:
当线程调用d.wait时,它必须拥有d -的内在锁 否则抛出错误。在synchronized中调用等待 方法是一种获取内在锁的简单方法。
如果在wait()之前没有同步,可能会导致如下问题:
If the 1st thread goes into makeChangeOnX() and checks the while condition, and it is true (x.metCondition() returns false, means x.condition is false) so it will get inside it. Then just before the wait() method, another thread goes to setConditionToTrue() and sets the x.condition to true and notifyAll(). Then only after that, the 1st thread will enter his wait() method (not affected by the notifyAll() that happened few moments before). In this case, the 1st thread will stay waiting for another thread to perform setConditionToTrue(), but that might not happen again.
但是如果你把synchronized放在改变对象状态的方法之前,这就不会发生。
class A {
private Object X;
makeChangeOnX(){
while (! x.getCondition()){
wait();
}
// Do the change
}
setConditionToTrue(){
x.condition = true;
notifyAll();
}
setConditionToFalse(){
x.condition = false;
notifyAll();
}
bool getCondition(){
return x.condition;
}
}
@Rollerball是对的。调用wait(),以便线程可以等待某些条件的发生,当wait()调用发生时,线程被迫放弃它的锁。 要放弃一些东西,你需要先拥有它。线程首先需要拥有锁。 因此需要在同步方法/块中调用它。
是的,我同意上面所有关于潜在损害/不一致的答案,如果你没有检查同步方法/块中的条件。然而,正如@shrini1000所指出的,只是在同步块中调用wait()并不能避免这种不一致的发生。
这是一篇不错的文章。