我已经读过你应该在while循环中将Object.wait()
调用放入Java中。原因是这个线程可能被唤醒,你等待通知的条件仍然是假的(虚假的唤醒)。
Object.wait(long timeout)
怎么样?在这里,您不希望循环这个条件,因为您希望它在指定的时间后超时。但如果你不把它放在一个循环中,你怎么能确保它不会被提前吵醒呢?
答案 0 :(得分:14)
但是如果你不把它放在一个循环中那么你怎么能确保它不会被提前唤醒呢?
这是Java IMO的一个缺陷,尽管它可能是各种OS变量中底层线程支持的缺陷。我怀疑Java知道等待是否超时,但调用者没有办法在没有重新测试条件并专门测试时间的情况下弄明白。难看。
因此,您需要将wait(long timeout)
放在while
循环中,也测试,看看时间是否超过了超时时间。我知道没有别的方法可以做到这一点。
long timeoutExpiredMs = System.currentTimeMillis() + timeoutMs;
while (!condition) {
long waitMillis = timeoutExpiredMs - System.currentTimeMillis();
if (waitMillis <= 0) {
// timeout expired
break;
}
// we assume we are in a synchronized (object) here
object.wait(waitMillis);
// we might be improperly awoken here so we loop around to see if the
// condition is still true or if we timed out
}
答案 1 :(得分:2)
long deadline = now() + timeout;
synchronized(lock)
while( !condition() && now()<deadline )
lock.wait( deadline - now() );
if(condition())
...
else // timeout
...
答案 2 :(得分:2)
这是因为java有Mesa风格的显示器而不是Hoare风格的显示器。所以你需要等待一段时间。 请搜索字符串“因此,通常需要在下面的网页中将每个等待操作包含在这样的循环中”,
http://en.wikipedia.org/wiki/Monitor_(synchronization)#Nonblocking_condition_variables
。如果它是Hoare风格的显示器,那么你可以放弃等待。我将很快添加Mesa监视器的细节。这不是Java的缺陷。两种类型的显示器都有优点和缺点。
答案 3 :(得分:0)
在循环中调用等待并不仅仅是为了处理偶尔的虚假唤醒。在一般(非玩具示例)的情况下,多个线程争用锁定,当一个线程从等待中醒来时,等待之前所做的任何检查都不足以预测等待后对象在中的状态。正在等待的线程放弃了锁定,因此从那时起就发生了任何事情,而且对于通知如何工作没有任何原子,只是因为你得到通知并不意味着另一个线程没有潜行在通知发出和通知线程重新获取锁定之间的时间内。
从根本上说,你在循环中等待,因为一旦你重新获得锁定,你需要检查当前状态,以便能够分辨出正在进行的操作。超时并没有改变。超时是一种安全机制,因此如果错过通知,线程将永远不会挂起。如果等待时间通常不需要任何特定操作,只需重新获取锁定,继续循环体,并像往常一样检查条件。
这不是Java的错,这就是pthreads的工作方式。