Java,等待和notifyAll:防止虚假的唤醒

时间:2016-01-30 23:49:35

标签: java multithreading wait notify

我有几个线程做了一些工作,然后必须去睡觉/等待一段不确定的时间。后来他们都需要被唤醒并恢复工作。我可以通过在对象上调用wait()然后在需要恢复时在同一对象上调用notifyall()来执行此操作。在研究此问题时,我发现了本教程:http://tutorials.jenkov.com/java-concurrency/thread-signaling.html 显然,通过将信号存储在信号类中并在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 loop。

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

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类?为什么我不能在wait类本身上拨打notifyMyWaitNotify

像这样:

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

这似乎有效,我不应该这样做吗?

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

对于这种用例,

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用作锁定对象。