在while循环中等待(长时间超时)?

问题描述 投票:12回答:4

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

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

java multithreading concurrency wait notify
4个回答
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
}

2
投票
long deadline = now() + timeout;

synchronized(lock)

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

    if(condition())
        ...
    else // timeout
        ...

2
投票

这是因为java有Mesa风格的显示器而不是Hoare风格的显示器。所以你需要等待一段时间。请搜索字符串“因此,通常需要在下面的网页中将每个等待操作包含在这样的循环中”,

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

。如果它是Hoare风格的显示器,那么你可以放弃等待。我将很快添加Mesa监视器的细节。这不是Java的缺陷。两种类型的监视器都有优点和缺点。


0
投票

在循环中调用等待不仅仅是为了处理偶尔的虚假唤醒。在一般(非玩具示例)情况下,多个线程争用锁定,当一个线程从等待中醒来时,它在等待之前进行的任何检查都不足以预测该对象在等待之后处于什么状态。正在等待的线程已放弃锁定,因此从那时起就发生了任何事情,此外,关于通知如何工作没有任何原子,只是因为你得到通知并不意味着另一个线程没有在通知之间的时间里潜入已经完成并且通知的线程重新获得了锁。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.