Java notify() run before wait()?
Asked Answered
C

3

9
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?

Chancey answered 23/10, 2013 at 9:15 Comment(5)
There is no wait or notify in your example...Floydflss
There are voices discouraging the use of wait/notify and proposing the use of for example CyclicBarrier or CountdownLatchGalla
If there was. (?), you could use java.util.concurrent.Semaphore. Init the 'permits' to 0, acquire() in the signaled thread, release() in the signaler thread. A semaphore retains the count, so it does not matter if the signal occurs before the wait - it will be stored.Mangrove
In fact, there is a very broad variety of possible solutions to your requirement.Galla
I am so sorry everyone! I put in the wrong code! Now changed!Chancey
C
15

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

C answered 23/10, 2013 at 9:56 Comment(1)
put it simple: while(true){synchronized(lock){while(!condition){lock.wait()}}}Olszewski
U
1

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) {
        }
    }
}
Undergrown answered 23/10, 2013 at 9:46 Comment(0)
P
0

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);
Plangent answered 23/10, 2013 at 9:23 Comment(3)
Problem is wait() should execute after notify(), in your code wait() always executes last.Lengthwise
@Aslamanwer The problem that the OP asked for is that when wait is called after notify then ThreadA will wait forever, because ThreadB is gone and thus will never call a notify on the synchronization monitor again. Nevertheless the problem the OP tries to solve is that ThreadA should always wait until ThreadB is finished. That's what join is about, because it does a loop and checks if the thread is still alive. If so it waits. Thus I don't understand your comment.Flory
Hi Rene, you are right, I didn't see your full code.Lengthwise

© 2022 - 2024 — McMap. All rights reserved.