14

我读过你应该Object.wait()在一个while循环中调用Java。原因是该线程可能被唤醒,而您等待通知的条件仍然为假(虚假唤醒)。

怎么样Object.wait(long timeout)。在这里,您不想循环条件,因为您希望它在指定的时间后超时。但是如果你不把它放在一个循环中,你怎么能保证它不会被提前唤醒呢?

4

4 回答 4

14

但是如果你不把它放在一个循环中,你怎么能保证它不会被提前唤醒呢?

这是 Java IMO 的一个缺陷,尽管它可能是各种操作系统变体中底层线程支持的一个缺陷。我怀疑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
}
于 2012-10-24T20:35:38.137 回答
2
long deadline = now() + timeout;

synchronized(lock)

    while( !condition() && now()<deadline )
        lock.wait( deadline - now() );

    if(condition())
        ...
    else // timeout
        ...
于 2012-10-24T20:37:43.643 回答
2

这是因为 java 有 Mesa 风格的监视器而不是 Hoare 风格的监视器。所以你需要把等待放在一个while循环中。请在以下网页上搜索字符串“为此,通常需要将每个等待操作都包含在这样的循环中”,

http://en.wikipedia.org/wiki/Monitor_(synchronization)#Nonblocking_condition_variables

.如果它是 Hoare 风格的显示器,那么您可以等待。我将很快添加 Mesa 监视器的详细信息。这不是 Java 的不足。两种类型的显示器都有优点和缺点。

于 2013-09-20T21:15:00.940 回答
0

在循环中调用 wait 不仅仅用于处理偶尔的虚假唤醒。在多个线程竞争锁的一般(非玩具示例)情况下,当线程从等待中唤醒时,它在等待之前所做的任何检查都不足以预测对象在等待之后的状态。正在等待的线程已经放弃了锁,所以从那时起任何事情都可能发生,而且通知的工作方式并没有原子性,仅仅因为你得到通知并不意味着另一个线程没有在通知之间的时间内潜入并且通知的线程重新获得了锁。

从根本上说,你在一个循环中等待,因为一旦你重新获得了锁,你需要检查当前状态,以便能够知道发生了什么。超时不会改变这一点。超时是一种安全机制,因此如果错过通知,线程不会永远挂起。如果等待超时,通常不需要任何特定操作,只需重新获取锁,继续循环体,并像往常一样检查条件。

这不是 Java 的错,这就是 pthread 的工作方式。

于 2017-07-25T13:45:09.593 回答