sgarg
sgarg

Reputation: 2459

Java notify() gets called before wait()

Isn't it possible that notify() in another thread gets called before the wait() in one thread? It's happening with me.

A client requests a value from a target and waits on a result variable RV. In case the target is the client itself, I update RV with the correct result and call notify() on RV in another thread.

class EMU {

  ResultVar RV;
  Address my_address;

  ResultVar findValue(String key) {
    String tgt = findTarget(key);
    sendRequest(tgt, key);
    synchronized(RV) {
      RV.wait();
    }

    return RV;
  }

  Runnable Server = new Runnable() {
    public void run() {
      //code to receive connections. Assume object of type Request is read from the stream.
      Request r = (Request) ois.readObject();
      if(r.requesterAddr.compareTo(my_address) == 0) {
        String val = findVal(key);
        RV.putVal(val);
        synchronized(RV){
          RV.notify();
        }
      }
    }
  };
}

The problem is that before the requester has completed all the "networking" (sendReqest in the above example) with itself, the result is updated in the result variable. When the requester thread now calls wait(), the program doesn't continue, since notify has already been called.

How can we prevent it?

Upvotes: 5

Views: 10074

Answers (5)

Grim
Grim

Reputation: 1976

Let me first break down the code to a minimum reproducable:

public static void main(String[] args) throws Exception {
    Object RV = new Object();
    new Thread() {
        @Override
        public void run() {
            synchronized (RV) {
                RV.notify();
            }
        }
    }.start();
    Thread.sleep(1_000);
    synchronized (RV) {
        RV.wait();
    }
}

This method will theoretically never end and the program will never quit. It shall be a dispute if this is a deadlock.

My solution is to create a second lock:

public static void main(String[] args) throws Exception {
    Object RV = new Object();
    Object lock = new Object();
    new Thread() {
        @Override
        public void run() {
            synchronized (lock) {
                lock.wait();
            }
            synchronized (RV) {
                RV.notify();
            }
        }
    }.start();
    Thread.sleep(1_000);
    synchronized (RV) {
        synchronized (lock) {
            lock.notify();
        }
        RV.wait();
    }
}

Lets inspect what the threads are doing while the main-thread is waiting one second:

  1. The custom Thread will first join the synchronized(lock) block.
  2. Then the lock will causes the custom Thread to wait.
  3. After 1 second the main-thread is joining a RV-synchronization.
  4. The lock gets notified and causes the custom Thread to continue the work.
  5. The custom thread leaves the synchronized(lock) block.
  6. The main thread will RV-wait-lock.
  7. The custom thread notifies the RV-lock to continue.

The program ends.

Upvotes: 0

Mak
Mak

Reputation: 616

Use some condition before going to wait() and make sure that condition is thread safe :)

class EMU{
    ResultVar RV;
    Address my_address;
    volatile boolean condition = true;

    ResultVar findValue(String key){
        String tgt = findTarget(key);
        sendRequest(tgt, key);
        synchronized(RV){
            while(condition == true)
            {
                RV.wait();
            }
        }
        return RV;
    }

    Runnable Server = new Runnable(){
        public void run(){
            //code to receive connections. Assume object of type Request is read from the stream.
            Request r = (Request) ois.readObject();
            if(r.requesterAddr.compareTo(my_address) == 0){
                String val = findVal(key);
                RV.putVal(val);
                synchronized(RV){
                    condition = false;
                    RV.notify();
                }
            }
        }

    };

Upvotes: 0

Bill Michell
Bill Michell

Reputation: 8360

I'd strongly recommend not re-inventing the wheel.

Java's Future interface is designed for results that may only arrive later, and the FutureTask class implements this interface.

Have the first thread obtain access to the Future and get the second thread to run the FutureTask, and all of this stuff gets handled for you. You also get timeout support for free.

Upvotes: 2

Joni
Joni

Reputation: 111239

Nothing stops you calling notify on an object that's not being waited by another thread.

It sounds like what you want is a wait only if some condition holds. For example:

synchronized (results) {
    while (!results.hasResults()) {
        // no results yet; wait for them
        try {
            results.wait();
        } catch (InterruptedException ie) { /* ignore */ }
    }
}

Upvotes: 5

Walter Laan
Walter Laan

Reputation: 2976

You check some flag before waiting (in a loop), see the tutorial: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

Upvotes: 7

Related Questions