【问题标题】:while(condition) { Object.wait() } idiomwhile(condition) { Object.wait() } 成语
【发布时间】:2014-02-12 07:31:03
【问题描述】:

我知道,我们使用这个习惯用法来等待通知来处理虚假唤醒:

synchronized (obj) {
    while(somecond)
        obj.wait();
}

如果出现虚假唤醒,我们将只检查状态并返回等待。

但是,考虑一下情况:

  1. 我们开始等待,obj.wait() 释放对 obj 的锁定。
  2. 等待线程被操作系统虚假通知
  3. 我们返回检查条件(由于等待释放了 obj 锁)
  4. obj.notify() 在那一刻被调用。

是的,条件检查非常快,我们可以进行条件检查而不是obj.wait() 的机会很小,可以忽略不计。在这种情况下,我们可以放弃obj.notify() 呼叫。

我是不是误解了什么,或者我们真的可以使用这种模式来放松通知?

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    另一个线程需要锁定obj 才能调用obj.notify()。如果您的线程在 while 循环中没有等待,它就不能拥有它,因为您的线程还需要锁定 obj 才能在 while 循环中。

    【讨论】:

    • 但是,调用 obj.wait() 不是释放 obj 的锁定吗?或者 obj 再次被锁定,因为我们得到虚假唤醒,离开等待并转移到条件检查?
    • 释放它,但是为了能够执行wait()调用之后的操作,它需要重新获取obj上的锁。同步块中的每条指令只有在线程持有锁的情况下才能执行。
    【解决方案2】:

    在调用obj.notify() 之前,不会返回对obj.wait() 的调用。但是,如果 另一个 线程也在等待并且系统决定通知该线程,您可能无法响应 obj.notify()。如果你想避免这种情况,你可以使用obj.notifyAll()。如果只有一个线程在等待,则不会丢失此模式的通知。

    注意,其他线程不能调用obj.notify(),除非它持有锁。如果这个线程忙于检查条件,那么它就拥有了锁,而另一个线程不能发出通知。 synchronized 块对于操作至关重要。

    【讨论】:

      【解决方案3】:

      在您介绍的情况下,线程 A 正在评估条件,线程 B 正在调用 notify,因此线程 A 错过了 notify 调用

      这种情况不可能调用 notify,因为它必须拥有线程 A 在同步块中使用的锁 - 只有一个线程可以同时拥有该锁。有关详细信息,请参阅javadoc on notify

      【讨论】:

        【解决方案4】:

        应该对我们正在检查的状态进行修改,而obj 上的锁定由之后调用obj.notify() 的任何人持有。所以,假设我们当前正在检查状态,我们也持有obj 上的锁。

        如果我们得到一个虚假的唤醒,并且状态没有改变,那么没有人应该打电话给obj.notify()。如果状态发生了变化,而我们错过了 obj.notify(),这无关紧要:对于所有意图,虚假唤醒和调用 obj.notifiy() 唤醒现在具有相同的效果。

        教训是,我们正在检查的状态应该只改变,而改变状态的人会锁定我们正在等待的对象。

        【讨论】:

          【解决方案5】:

          由于大多数答案都坚持认为该场景是不可能的,因此值得对其进行调整:

          总是可以在没有匹配的waiting 线程的情况下调用notify。这可能发生在通知线程调用notify 之前 另一个线程甚至进入整个synchronized 块。 notify 方法甚至可能在任何线程进入 synchronized 块到 wait 之前被多次调用,wait-notify 机制不会计算这些。

          因此,您必须处理错过notify 的情况,例如在调用wait 之前检查synchronized 块中的条件。但是通过这样做,您可以在匹配的notify 确实处于未决状态时处理和重置条件。

          因此,您必须始终注意以下两种情况的可能性

          • 您可能错过了一个或多个 notify 调用
          • 您可能会收到一个过时的notify

          这就是为什么正确的处理循环是这样的原因

          synchronized(obj) {
              while(somecond)
                  obj.wait();
          }
          

          从应用程序的角度来看,过时的待处理 notifys 和 JVM/OS 生成的虚假唤醒没有关联的 notify 调用之间没有区别。这就是为什么没有尝试防止 JVM 进行虚假唤醒的原因。因为逻辑不会改变,所以努力会白费。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-09-04
            • 2013-02-05
            • 2014-01-14
            • 2010-11-25
            • 2018-07-10
            • 2021-08-26
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多