Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java notify() run before wait()?

public class ThreadA {
    public static void main(String[] args){
        ThreadB b = new ThreadB();
        b.start();

        synchronized(b){
            try{
                System.out.println("Waiting for b to complete...");
                b.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
}

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;
            }
            notify();
        }
    }
}

As the example above, if wait() block enter first, the subsequent notify() in ThreadB will tell the Main Thread to continue.

But we cannot guarantee wait() will execute before notify(), what if ThreadB enter the block first? Notify() will execute before wait(), so wait() will hang there forever (because no more notify() to tell it to continue)? What usually is the proper way to handle this?

like image 505
Sam YC Avatar asked Oct 23 '13 09:10

Sam YC


2 Answers

You should almost always have a predicate together with wait/notify. That is, you need a condition that you can check, such as a variable becoming true, a queue becoming empty/full etc. Just blindly waiting for someone to call .notify() have very few use cases.

So, The following is Not OK, for the reason you say, the other thread could call .notify() before ThreadA calls .wait()

public class ThreadA {
    public static Object latch = new Object();
    public static void main(String[] args) {
        ThreadB b = new ThreadB();
        b.start();
        synchronized(latch ) {
            latch.wait(); //wait for B to finish a calculation
        }
        System.out.println("Total is: " + b.total);

    }
}

class ThreadB extends Thread {
    int total;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            total += i;
        }
       synchronized(ThreadA.latch) {
           ThreadA.latch.notify();
       }
    }
}

You need to do something like this:

 public class ThreadA {
    public static Object latch = new Object();
    public static boolean done = false;
    public static void main(String[] args) {
        ThreadB b = new ThreadB();
        b.start();
        synchronized(latch ) {
            while (!done) {   //wait for B to indicate it is finished.
                latch.wait(); 
            }
        }
        System.out.println("Total is: " + b.total);

    }
}

class ThreadB extends Thread {
    int total;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            total += i;
        }
       synchronized(ThreadA.latch) {
           ThreadA.done = true;         
           ThreadA.latch.notify();
       }
    }
}

Note that in the above, the done variable is protected by the synchronized block, .wait() will atomically release/re-aquire that lock. So there is no race condition, and if .notify() is called before we get to the .wait() call , ThreadA will discover that because done will be true and not enter the .wait() call at all.

For a simple case such as this code, you can just wait for ThreadB to to end, can be done with b.join();

like image 190
nos Avatar answered Nov 07 '22 07:11

nos


One of the many possible solutions for your problem is:

public class ThreadA {
  public static final CyclicBarrier barrier = new CyclicBarrier(2);

  public static void main(String[] args) {
    ThreadB b = new ThreadB();
    b.start();
    try {
      barrier.await();
      System.out.println("Total is: " + b.total);
    } catch (InterruptedException | BrokenBarrierException ex) {
    }
  }
}

class ThreadB extends Thread {
    int total;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            total += i;
        }
        try {
          ThreadA.barrier.await();
        } catch (InterruptedException | BrokenBarrierException ex) {
        }
    }
}
like image 20
TwoThe Avatar answered Nov 07 '22 07:11

TwoThe