【问题标题】:wait(long timeout) in a while loop?在while循环中等待(长时间超时)?
【发布时间】:2012-10-14 23:12:12
【问题描述】:

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

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

【问题讨论】:

    标签: java multithreading concurrency wait notify


    【解决方案1】:

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

    这是 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
    }
    

    【讨论】:

    • 不确定是否存在缺陷。假设我们知道唤醒的确切原因,你能用它做什么?无论如何,我们必须重新检查条件。
    • 如果等待返回一个布尔值@irreputable,那么您至少不需要测试过期时间。你会知道你是否收到通知。
    • 即使唤醒是由于超时,条件仍然可以成立。因为我们希望这里的条件为真,所以无论如何我们都应该重新检查它(检查通常很便宜)。
    • 我当然知道“虚假唤醒”@PavelZdenek。我所说的缺陷是wait(timeout) 方法无法报告它是否超时。我怀疑它知道。顺便说一句,每个人都在谈论虚假唤醒,但while 循环的真正原因是生产者/消费者竞争条件。见这里:256.com/gray/docs/misc/producer_consumer_race_conditions
    • 您的代码中有一个问题,当计算的差异变为负数时,object.wait(...) 可能会抛出 IllegalArgumentException。例如,如果在 timeoutExpiredMs - 5 发生虚假超时,超时检查发生在 timeoutExpiredMs 之前,条件为 false 并且直到下一个 object.wait(...) 被执行,超过 5 毫秒通过(例如,另一个线程被调度),就会发生这种情况)
    【解决方案2】:
    long deadline = now() + timeout;
    
    synchronized(lock)
    
        while( !condition() && now()<deadline )
            lock.wait( deadline - now() );
    
        if(condition())
            ...
        else // timeout
            ...
    

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-07-18
          • 2020-05-09
          • 1970-01-01
          • 2021-04-27
          • 1970-01-01
          • 1970-01-01
          • 2016-07-05
          • 2016-12-05
          相关资源
          最近更新 更多