BruceB
BruceB

Reputation: 47

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. 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. 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(); with 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.

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.

Does anyone have a solution that will enable me to use both notify or notifyAll on the threads that is waiting?

And one thing about the example I do not understand, why do I have to use a separate MonitorObject class at all? Why can't I just call wait and notify on the MyWaitNotify class itself?

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?

Upvotes: 0

Views: 431

Answers (2)

ZhongYu
ZhongYu

Reputation: 19682

Phaser is a good high-level tool for this kind of use case.

    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. If you do synchronized(this), there is a chance that someone else may also want to use this as a lock object.

Upvotes: 0

David Schwartz
David Schwartz

Reputation: 182829

Use a generation integer. When a thread blocks, block until the generation integer changes. Before calling notifyAll, increment the generation integer.

Upvotes: 1

Related Questions