while(condition){Object.wait()} idiom

时间:2014-02-12 07:31:03

标签: java multithreading

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

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

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

但是,考虑一下情况:

  1. 我们开始等待,并且obj.wait()释放对obj的锁定。
  2. 操作系统虚假通知等待线程
  3. 我们返回检查条件(由于等待而释放obj锁)
  4. obj.notify()在那一刻被称为正确。
  5. 是的,条件检查非常快,我们可以进行状态检查而不是obj.wait(),可能性非常小。在这种情况下,我们可以放松obj.notify()来电。

    我是否误解了某些内容,或者我们真的可以使用此模式忽略通知?

5 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:1)

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

请注意,除非持有锁,否则其他线程无法调用obj.notify()。如果此线程正忙于检查条件,则它具有锁定,而另一个线程无法发出通知。 synchronized块对于操作至关重要。

答案 2 :(得分:1)

在你出现的情况下,线程A正在评估条件,线程B正在调用notify,以致线程A错过了notify调用

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

答案 3 :(得分:1)

我们正在检查对状态的修改,而obj上的锁定由之后调用obj.notify()的任何人保留。因此,假设我们目前正在检查状态,我们也会锁定obj

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

经验教训是,我们正在检查的状态只能被更改,而改变状态的人会锁定我们正在等待的对象。

答案 4 :(得分:1)

由于大多数答案都坚持不可能出现这种情况,因此值得调整:

在没有匹配的notify线程的情况下,始终可以调用wait。当通知线程在其他线程甚至进入整个notify块之前调用synchronized 时,可能会发生这种情况。在任何线程进入notify阻止synchronized阻止wait之前,甚至可能多次调用wait方法,并且notify - notify机制不计算这些

因此,您必须处理错过synchronized的案例,例如通过在调用wait之前检查notify块内的条件。但通过这样做,您可以添加处理和重置条件的可能性,而匹配的notify确实正在等待。

因此,你必须始终意识到

的可能性
  • 您可能错过了一次或多次notify次调用
  • 您可能会收到过时的synchronized(obj) { while(somecond) obj.wait(); }

这就是为什么正确的处理循环喜欢那样

notify

从应用程序的角度来看,过时的待定notify与没有关联{{1}}调用的JVM / OS生成的虚假唤醒之间没有区别。这就是为什么没有尝试防止JVM的虚假唤醒。由于逻辑不会改变,这种努力将被浪费。