JensD JensD - 4 months ago 11
Java Question

Threads notifyAll()

Given a thread has multiple states: alive runnable running waiting and terminated.
the notifyAll() method is suppose to put all threads that are "waiting" on an object's lock back into the "runnable" state where it is may be chosen as the next running object.
The following example instantiates and starts 3 Reader threads, which go into waiting (wait() method) until the 'calc' object's lock is released. The calc objects thread is instantiated and started just after this where it adds up some numbers, followed by notifyAll().

My question is, why doesn't the calc thread notify all the Reader threads every time? when I run this on my computer it's hit and miss.

public class Reader extends Thread{
Calculator c;

public Reader(Calculator calc){
c=calc;
}

public void run(){
synchronized(c){
try{
System.out.println("Waiting for calculation...");
c.wait();
}catch(InterruptedException e){}
System.out.println("Total is: "+c.total);
}
}
public static void main(String[] args){
Calculator calc = new Calculator();
new Reader(calc).start();
new Reader(calc).start();
new Reader(calc).start();
new Thread(calc).start();
}
}
class Calculator implements Runnable{
int total;

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

Answer

When executing multiple threads, order of execution of the threads is not guaranteed.

In your case chances are that the Calculator thread completes it's loop and calls notifyAll() even before any of the Reader threads gets into runnable state. So all Reader will keep waiting and will never print total.

To avoid such situation, in this particular example you can use another flag isCalculated in Calculator and set this flag once computation is done. Reader threads will also check for this flag and wait only when isCalculated is false.

class Reader extends Thread {
  Calculator c;

  public Reader(Calculator calc) {
    c = calc;
  }

  public void run() {
    synchronized (c) {
      try {
        System.out.println("Waiting for calculation...");

        if (!c.isCalculated) { // wait only if calculation is not done
          c.wait();
        }
      } catch (InterruptedException e) {
      }
      System.out.println("Total is: " + c.total);
    }
  }
}

class Calculator implements Runnable {
  int total;
  boolean isCalculated;

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

      isCalculated = true;  // set flag to mark that computation is complete
      notifyAll();
    }
  }
}