Sam YC
Sam YC

Reputation: 11617

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?

Upvotes: 9

Views: 7759

Answers (3)

nos
nos

Reputation: 229088

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

Upvotes: 15

TwoThe
TwoThe

Reputation: 14259

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) {
        }
    }
}

Upvotes: 1

Ren&#233; Link
Ren&#233; Link

Reputation: 51353

I guess you want to do something like this

public class ThreadA {
    public static void main(String[] args) {
        ThreadB b = new ThreadB();
        b.start();
        b.join(); // Wait for thread b to finish 

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

    }
}

You should also let ThreadB only implement Runnable and not extend Thread.

class ThreadB implements Runnable {
    int total;

    public void run() {
        for (int i = 0; i < 100; i++) {
            total += i;
        } 
    }
}

and then use it

ThreadB tb = new ThreadB();
Thread b = new Thread(tb);

Upvotes: 0

Related Questions