This is some information on the Java 6 certification that I prepare. This information is not very well structured and it is most of the time an answer to the questions that come when I read the SCJP 6 book (Bates and Sierra). *** AS OF 5 OF MARCH 2010, I AM SCJP 6.0 ***

Tuesday, January 26, 2010

24 - Thread : notify/notifyall- level basic - Java 1.6.0_17 - NB 6.8

(
after all the Master Exam questions, they ask you several times to choose notify or notifyAll ...
most of the time if you answer (please read the question carefully) notifyAll, it is the wrong answer...
why ? it is because there is a producer and several consumers... after the producer produced something the producer will notify the consumers... the way they wrote the example, they do a wait but as soon as the thread is notified they do not check that something was produced (in the consumer thread)
if the producer do notifyAll, then (and it is not new) only 1 thread continues in the synchronized block...
this thread will consume what was produced and exit the synch block and it is fine...
but if notifyAll was used by the producer, then the second, third,... thread (one of those) are now notified but... nothing was produced and a thread will...consume (without checking that something was produced) and...that can cause a IndexOutOfBoundsException to be thrown if the thread calls (by ex) refArrayList.remove(0) without checking that the ArrayList is empty... If you answer notify, then only one thread will be notified and then consume... Then the producer after a while (sometimes they put a sleep) will produce and then call notify...
imho you serialize the way you produce and consume


you produce
then notify1 amongst all the threads
...

or
you produce
then notifyAll

all the threads will be able to continue before the producer produced (one by one in the synch blcok)
and that can cause a problem
(I repeat you have to find a way that the producer made something new)

*** Hope this help ***

)



3 classes. A Main that will start the threads. A producer (calling notify/notifyall) and  consumers.

The Producer

package threadnotifyall;

public class Calculator extends Thread {

    int total;

    public void run() {
        try {
            System.out.println("Calculator...");
            Thread.currentThread().sleep(1000);
            synchronized (this) {
                for (int i = 0; i < 100; i++) {
                    total += i;
                }
                Thread.currentThread().sleep(5000);
                notifyAll(); // OR
                //notify();
            }
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
    }
}

The Consumer

package threadnotifyall;

public class Reader extends Thread {
    Calculator c; // reference to the producer

    public Reader(Calculator c) {
        this.c = c;
    }

    public void run() { // it is a thread 
        synchronized(c) { // lock the producer (try to lock)
            try {
                System.out.println(Thread.currentThread().getName() + " waiting for calculation...");
                c.wait(); // if the producer is locked but did not notify (us) wait to be notified (1 and only 1 thread !) or wait forever (see my remarks later)
            } catch(InterruptedException ie) {
                ie.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()  + " Total is :" + c.total); // show the thread that was notified
        }
    }
}

The bandmaster

package threadnotifyall;

public class Main {

    public static void main(String[] args) {
      
        Calculator calculator = new Calculator(); // the producer

        new Reader(calculator).start(); // 3 consumers threads (we pass the producer they have to wait for)
        new Reader(calculator).start();
        new Reader(calculator).start();
        calculator.start(); // the 3 consumers were started (all waiting and the producer shows up)
        System.out.println("end of main"); // end of the THREAD main (because main is a THREAD)
    }
}

With notify

IT IS AN ASSUMPTION BECAUSE THE WAY THE JAVA SCHEDULER WILL CHOOSE THE THREAD DEPENDS ON A FEW FACTORS (WE CAN INFLUENCE)

run:
Thread-1 waiting for calculation...
Thread-2 waiting for calculation...
Thread-3 waiting for calculation...
end of main
Calculator...
Thread-1 Total is :4950
Thread-3 Total is :4950
Thread-2 Total is :4950
BUILD SUCCESSFUL (total time: 5 seconds)

run:
Thread-1 waiting for calculation...
Thread-2 waiting for calculation...
end of main
Thread-3 waiting for calculation...
Calculator...
Thread-1 Total is :4950
Thread-3 Total is :4950
Thread-2 Total is :4950
BUILD SUCCESSFUL (total time: 5 seconds)

I did several executions and can report that if we use notify, it is always the first thread that was started that will be choosen (notified that the producer has something to deliver). In the output as it is Thread-1 that was the first to be started (and waiting), Thread-1 is the first to be notified... You can see that the main thread terminated BEFORE the 3 threads were terminated. But Java is polite :-) and will wait that the 3 consumers threads will terminate.

With notifyall

run:
Thread-1 waiting for calculation...
Thread-2 waiting for calculation...
end of main
Thread-3 waiting for calculation...
Calculator...
Thread-3 Total is :4950
Thread-2 Total is :4950
Thread-1 Total is :4950
BUILD SUCCESSFUL (total time: 5 seconds)

run:
Thread-1 waiting for calculation...
Thread-3 waiting for calculation...
Thread-2 waiting for calculation...
end of main
Calculator...
Thread-2 Total is :4950
Thread-3 Total is :4950
Thread-1 Total is :4950
BUILD SUCCESSFUL (total time: 5 seconds)

run:
Thread-1 waiting for calculation...
Thread-3 waiting for calculation...
Thread-2 waiting for calculation...
end of main
Calculator...
Thread-2 Total is :4950
Thread-3 Total is :4950
Thread-1 Total is :4950
BUILD SUCCESSFUL (total time: 5 seconds)

As you can see using notifyall, it is the last thread that was started that was notified this time.

Following my experimentation with the kind of JVM that I use
  • If notify is used, then it is the thread that was the FIRST to be waiting that will be notified. Then the second,... Then the third... until all the threads waiting to be notified.
  • If notifyall is used, then it is the thread that was the LAST to be waiting that will be notified, then the previous, etc.
  • The Java scheduler will choose a thread to be notified (there is a list of threads awaiting) and the one that is awaken continues JUST AFTER the wait !!! But the awaken thread does not have to execute "synchronized" to get the lock. The Java scheduler guaranty the thread to receive the lock if the thread is chosen. ALL the other threads ARE WAITING to be notified (IMHO there are NOT in the runnable queue; the threads are in a special queue). They will all be notified ONE BY ONE. The way there are chosen depends on notify / notifyall. But the mechanism to awake the threads is the same. One by one !!! But look in the beg of this post, if nothing was produced and the thread that is awaken does not check that something is produced (with notifyAll) you can cause a IndexOutOfBoundsException to be thrown !!!
  • If you inverse the consumers and the producers meaning you start the producer first and later the consumer and let's say you also put a sleep just after the producer finishes. The consumers will ALL wait to be notified but will never be notified because the producer finished before any consumers were started. You see that the main thread is finished, but all the others keep waiting FOREVER !!! Thus the sequence of events is important and it is written  in the Sierra/Bates book that you have to wait for something that will happen (by example there is something to be read  from a queue and it is your application queue) but NOT unconditionaly !!!


1 comment:

  1. Hi, Just to add while using wait and notify or notifyAll method in Java following things must be remember :

    1) use notifyAll instead of notify if you expect more than one thread is waiting for lock.
    2)wait() and notify() method must be called from synchronized context, see the link for more detailed explanation.
    3) Always call wait() method in loop because if multiple threads are waiting for lock and one of them got lock and reset the condition and other thread needs to check the condition after they got wake up to see whether they need to wait again or can start processing.
    4) use same object for calling wait() and notify() method, every object has its own lock so calling wait() on objectA and notify() on object B will not make any sense.

    ReplyDelete

Followers