Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Notify Threads When Counter Changes

I am trying to design a class as a Code Kata for myself that has a value property that can be set, and the class can issue ValueListener instances. The idea is that there is one instance of ValueHolder with many client threads accessing it concurrently. Each client thread has requested a ValueWatcher and has called waitForValue().

What I am really struggling with is what condition I should use on the while loop around the wait() to avoid spurious notifications (i.e. value hasn't changed). I can see that this design may make it possible of ValueWatcher instances to miss updates, but am less worried about that at this stage.

Would appreciate any guidance on offer!

public class ValueHolder {

  private int value = 0;
  private final Object monitor = new Object();

  public void setValue(int value) {
    synchronized(monitor) {
      this.value = value;
      monitor.notifyAll();
    }
  }

  ValueWatcher createChangeWatcher() {
    return new ValueWatcher();
  }

  private class ValueWatcher {
    public int waitForValue() {
      synchronized(monitor) {
        while (==== ??? =====) {
          monitor.wait();
          return value;
        }
      }
    }
  }     
}
like image 412
Scruffers Avatar asked Mar 06 '12 22:03

Scruffers


People also ask

Can a thread notify itself?

You can't get a Thread to notify itself since it is blocked in wait() . You can have another thread notify the thread by synchronizing on the same object that the thread is locking on and calling notify() . See the below code for a sample. That said, I'd recommend using a BlockingQueue to share data in this respect.

What is notify in multithreading?

notify method wakes up only one thread waiting on the object and that thread starts execution. So if there are multiple threads waiting for an object, this method will wake up only one of them. The choice of the thread to wake depends on the OS implementation of thread management.

How does notify all work?

notifyAll(): The notifyAll() wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the wait methods. The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object.


2 Answers

Interesting problem. Here's one solution off the top of my head. Have a version number along with the value that is being changed. Whenever the value is updated, the version number is also incremented so the ValueWatcher objects can then check to see if the version went up meaning a change has happened.

Edit: I initially had an AtomicLong but I am stealing the idea of a wrapper object from @John Vint.

private final VersionValue versionValue = new VersionValue();

public void setValue(int value) {
    synchronized (monitor) {
       versionValue.value = value;
       versionValue.version++;
       monitor.notifyAll();
    }
}

 private class ValueWatcher {
     private long localVersion = 0;
     public int waitForValue() {
         synchronized (monitor) {
             while (true) {
                 if (localVersion < versionValue.version) {
                     // NOTE: the value might have been set twice here
                     localVersion = versionValue.version;
                     return versionValue.value;
                 }
                 monitor.wait();
             }
         }
     }
}

private static class VersionValue {
    int value;
    long version;
}

Also, although spurious wakeups are possible it is important to remember that the text:

Always invoke wait inside a loop that tests for the condition being waited for. Don't assume that the interrupt was for the particular condition you were waiting for, or that the condition is still true.

Is more about race conditions and producer/consumer models than spurious wakeups. See my page here about that.

like image 198
Gray Avatar answered Sep 29 '22 23:09

Gray


All you really care about is if the value changed after you enter the method and its synchronized block. So take a timestamps of the last time a value has been changed and only continue when the last updated timestamp > then when you entered.

    private final StampedValue stamped = new StampedValue();

    public void setValue(int value) {
        synchronized (monitor) {
            this.stamped.value = value;
            this.stamped.lastUpdated = System.currentTimeMillis();
            monitor.notifyAll();
        }
    }
    private class ValueWatcher {

        public int waitForValue() { 
          synchronized(monitor) {
               long enteredOn = System.currentTimeMillis();    
               while (enteredOn > stamped.lastUpdated) {
                   monitor.wait();
               }
               return stamped.value;
          }
        }
    }
    private class StampedValue {
        long lastUpdated = System.currentTimeMillis();
        int value;
    }
like image 32
John Vint Avatar answered Sep 29 '22 21:09

John Vint