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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13057240/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 11:21:20  来源:igfitidea点击:

wait(long timeout) in a while loop?

javamultithreadingconcurrencywaitnotify

提问by AFS

I've read that you're supposed to put Object.wait()calls in Java in a while loop. The reason is that this thread may be woken up and the condition that you were waiting to notify on is still false (spurious wake-up).

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

What about Object.wait(long timeout). Here, you don't want to loop on the condition since you want it to time out after the specified amount of time. But if you don't put it in a loop then how can you ensure that it won't be woken up early?

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

回答by Gray

But if you don't put it in a loop then how can you ensure that it won't be woken up early?

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

This is a deficiency in Java IMO although maybe it's a deficiency with the underlying thread support in various OS varients. I suspect Java knows whether the wait timed out or not but there is no way for the caller to figure it out without re-testing the condition and specifically testing the time. Ugly.

这是 Java IMO 的一个缺陷,尽管它可能是各种操作系统变体中底层线程支持的一个缺陷。我怀疑 Java 知道等待是否超时,但调用者无法在不重新测试条件并专门测试时间的情况下弄清楚它。丑陋的。

So you will need to put the wait(long timeout)in a whileloop as well and alsotest to see if the time is past the timeout period. I know of no other way to accomplish this.

所以,你需要把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
}

回答by irreputable

long deadline = now() + timeout;

synchronized(lock)

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

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

回答by Mayur

it is because java has Mesa style monitors instead of Hoare style monitors. So you need to put wait in a while loop. Please search the string "For this reason, it is usually necessary to enclose each wait operation in a loop like this" on the follwing web page,

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

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

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

.if it had been a Hoare style monitors then you could have put your wait in if. I will soon add details of Mesa monitors. This is not deficiency in Java. Both types of monitors have advantages and disadvantages.

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

回答by Nathan Hughes

Calling wait in a loop isn't just for handling the occasional spurious wakeup. In the general (non-toy example) case where multiple threads are contending for the lock, at the time a thread wakes up from wait any checks it made prior to waitingaren't enough to predict what state the object is in after waiting. The thread that was waiting had relinquished the lock so anything could have happened since then, and moreover there's nothing atomic about how notifications work, just because you got notified doesn't mean another thread didn't sneak in in the time between when the notification was made and the notified thread reacquired the lock.

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

Fundamentally you wait in a loop because once you have reacquired the lock you need to check the current state, in order to be able to tell what's going on. Having a timeout doesn't change that. A timeout is a safety mechanism so that if a notification is missed the thread won't hang forever. If the wait times out there usually isn't any specific action needed, just reacquire the lock, proceed to the loop body, and check the condition as usual.

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

And this isn't Java's fault, this is how pthreads work.

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