Java,wait and notifyAll:防止虚假唤醒

Java, wait and notifyAll: guard against spurious wakeups

我有几个线程在做一些工作,然后必须转到 sleep/wait 一段时间未定。稍后他们都需要被唤醒并恢复工作。我可以通过在一个对象上调用 wait() 然后在需要恢复时在同一个对象上调用 notifyall() 来做到这一点。在研究这个问题时,我发现了这个教程:http://tutorials.jenkov.com/java-concurrency/thread-signaling.html 显然,通过将信号存储在信号 class 中并检查 while 循环中的信号成员变量来防止丢失信号和虚假唤醒是一种很好的做法。 这是教程中的代码示例:

public class MonitorObject{
}

public class MyWaitNotify3{

  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;

  public void doWait(){
    synchronized(myMonitorObject){
      while(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}

此代码有效,但我需要唤醒所有线程,而不仅仅是一个。如果我将 myMonitorObject.notify(); 替换为 myMonitorObject.notifyAll(); 将不起作用,因为恢复工作的第一个线程会将 wasSignalled 标志设置为 false 并且所有其他线程将被困在while 循环。

我做了一些更改,使我能够唤醒所有线程:

MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false;

public void doWait(){
    synchronized(myMonitorObject){
        while(!wasSignalled){
            try{
                myMonitorObject.wait();
            } catch(InterruptedException e){

            }
        }

    }
}

public void resetSignal() {
    wasSignalled = false;
}

public void doNotifyAll() {
    synchronized(myMonitorObject){
        wasSignalled = true;
        myMonitorObject.notifyAll();
    }
}

但这不是一个很好的解决方案,因为现在我不能只唤醒一个线程,我必须在doNotify之后重新设置信号才能再次使用doWait。

有没有人有解决方案可以让我在等待的线程上同时使用 notifynotifyAll

还有一件事我不明白这个例子,为什么我必须使用一个单独的 MonitorObject class?为什么我不能在 MyWaitNotify class 本身上调用 waitnotify

像这样:

public class WaitNotify {
    boolean wasSignalled = false;

    public void doWait(){
        synchronized(this){
            while(!wasSignalled){
                try{
                    wait();
                } catch(InterruptedException e){

                }
            }
        }
    }

    public void resetSignal() {
        wasSignalled = false;
    }

    public void doNotifyAll() {
        synchronized(this){
            wasSignalled = true;
            notifyAll();
        }
    }
}

这似乎有效,我有什么理由不应该这样做?

使用世代整数。当一个线程阻塞时,阻塞直到生成整数改变。在调用 notifyAll 之前,递增生成整数。

Phaser 是适合此类用例的高级工具。

    final Phaser phaser = new Phaser(1);

    doNotify()
        phaser.arrive();  // increase phase

    doWait()
        int phase = phaser.getPhase();
        phaser.awaitAdvance( phase );   // await phase change

synchronized 对私有对象的优势是其他人无法对它进行 synchronized。如果您这样做 synchronized(this),其他人可能也想使用 this 作为锁定对象。