简体   繁体   English

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

[英]Java, wait and notifyAll: guard against spurious wakeups

I have several threads that does some work, and then must go to sleep/wait for an undetermined time. 我有几个执行某些工作的线程,然后必须进入睡眠/等待时间不确定。 Later they all need to be waken up and resume their work. 后来,他们都需要被唤醒并恢复工作。 I can do this by calling wait() on an object and then notifyall() on the same object when they need to resume. 我可以通过在对象上调用wait() ,然后在需要恢复时在同一对象上调用notifyall()来做到这一点。 When researching this issue i discovered this tutorial: http://tutorials.jenkov.com/java-concurrency/thread-signaling.html Apparantly it is good practice to guard against missed signals and spurious wakeups by storing the signal inside the signal class and check the signal member variable inside a while loop. 研究此问题时,我发现了本教程: http ://tutorials.jenkov.com/java-concurrency/thread-signaling.html显然,通过将信号存储在信号类中来防止丢失信号和虚假唤醒是一种很好的做法。在while循环中检查信号成员变量。 Here is the code example from the tutorial: 这是教程中的代码示例:

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();
    }
  }
}

This code is working, but I need to wake up all threads and not just one. 这段代码有效,但是我需要唤醒所有线程,而不仅仅是一个。 If I replace myMonitorObject.notify(); 如果我替换myMonitorObject.notify(); with myMonitorObject.notifyAll(); myMonitorObject.notifyAll(); that will not work because the first thread that resumes work will set the wasSignalled flag to false and all the other threads will be trapped in the while loop. 这将无法正常工作,因为第一个恢复工作的线程会将wasSignalled标志设置为false而所有其他线程将被困在while循环中。

I have made some changes that will enable me to wake up all threads: 我进行了一些更改,使我可以唤醒所有线程:

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();
    }
}

But this is not a very good solution, because now I can't wake up just one thread, and I have to reset the signal after doNotify before I can use doWait again. 但这不是一个很好的解决方案,因为现在我不能只唤醒一个线程,并且必须在doNotify之后重置信号,然后才能再次使用doWait。

Does anyone have a solution that will enable me to use both notify or notifyAll on the threads that is waiting? 有没有人有一个解决方案可以使我在正在等待的线程上同时使用notifynotifyAll

And one thing about the example I do not understand, why do I have to use a separate MonitorObject class at all? 关于我不理解的示例的一件事,为什么我必须完全使用单独的MonitorObject类? Why can't I just call wait and notify on the MyWaitNotify class itself? 为什么我不能只调用wait并在MyWaitNotify类本身上进行notify

Like this: 像这样:

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();
        }
    }
}

This seems to be working, any reason I should not be doing this? 这似乎在起作用,有什么原因我不应该这样做?

Use a generation integer. 使用生成整数。 When a thread blocks, block until the generation integer changes. 当线程阻塞时,阻塞直到生成整数更改为止。 Before calling notifyAll , increment the generation integer. 在调用notifyAll之前,增加生成整数。

Phaser is a good high-level tool for this kind of use case. 对于这种用例, Phaser是很好的高级工具。

    final Phaser phaser = new Phaser(1);

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

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

synchronized on a privately owned object has the advantage that nobody else could do synchronized on it. 在私有对象上进行synchronized具有以下优点:没有其他人可以对此对象进行synchronized If you do synchronized(this) , there is a chance that someone else may also want to use this as a lock object. 如果您进行了synchronized(this) ,则其他人可能还会this对象用作锁定对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM