notify() behaving like notifyAll()

Ali :

I have one Calculator thread that calculates the sum of number from 1 to 50, and multiple Reader threads that show the result once Calculator thread is ready. I have an option of calling notify() and notifyAll() to signal the Reader threads that the result of calculation is ready to display. At LINE B of Calculator class, if I call the notifyAll() method, the result is printed 4 times as expected. But when I replace LINE B with just notify() still I see the result printed 4 times which does not seem to be correct. It is my understanding that notify() will only wakes up one of the threads that is waiting, not all. Why are all of the threads waking up and printing the result when I call notify?

public class ThreadWaitNotify {

    public static void main(String[] args) {
        Calculator c = new Calculator();
        Reader r = new Reader(c);
        Reader r2 = new Reader(c);
        Reader r3 = new Reader(c);
        Reader r4 = new Reader(c);

        r.start();
        r2.start();
        r3.start();
        r4.start();
        try {
            Thread.sleep(500L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        c.start();
    }

}

Reader class:

class Reader extends Thread {

    Calculator c;

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

    @Override
    public void run() {
        synchronized (c) {
            try {
                System.out.println(Thread.currentThread().getName() + " Waiting for calculations: ");
                c.wait();    // LINE A
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " Total: " + c.getSum());
        }
    }
}

Calculator class:

class Calculator extends Thread {

    private int sum = 0;

    @Override
    public void run() {
        synchronized (this) {
            for (int i = 1; i <= 50; i++) {
                sum += i;
            }
            notify();  // LINE B
        }
    }

    public int getSum() {
        return sum;
    }
}

Output:

Thread-1 Waiting for calculations: 
Thread-4 Waiting for calculations: 
Thread-3 Waiting for calculations: 
Thread-2 Waiting for calculations: 
Thread-1 Total: 1275
Thread-2 Total: 1275
Thread-3 Total: 1275
Thread-4 Total: 1275

======================

UPDATE: Using an object as a monitor/lock instead of a Thread instance produces the correct behavior.

Updated ThreadWaitNotify Class:

public class ThreadWaitNotify {

    public static void main(String[] args) {
        Object monitor = new Object();
        Calculator c = new Calculator(monitor);
        Reader r = new Reader(c, monitor);
        Reader r2 = new Reader(c, monitor);
        Reader r3 = new Reader(c, monitor);
        Reader r4 = new Reader(c, monitor);

        r.start();
        r2.start();
        r3.start();
        r4.start();
        try {
            Thread.sleep(500L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        c.start();
    }

}

Updated Reader Class:

class Reader extends Thread {

    Calculator c;
    Object monitor;

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

    @Override
    public void run() {
        synchronized (monitor) {
            try {
                System.out.println(Thread.currentThread().getName() + " Waiting for calculations: ");
                monitor.wait();   // LINE A
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " Total: " + c.getSum());
        }
    }
}

Updated Calculator Class:

class Calculator extends Thread {

    private int sum = 0;
    Object monitor;

    public Calculator(Object monitor) {
        this.monitor = monitor;
    }

    @Override
    public void run() {
        synchronized (monitor) {
            for (int i = 1; i <= 50; i++) {
                sum += i;
            }
            monitor.notify();       // LINE B
        }
    }

    public int getSum() {
        return sum;
    }
}
Izruo :

It's not the notify() that wakes up all your reader Threads, but the end of the Calculator's Thread's lifespan.

I didn't know about this behaviour until now, too, but it seems that a terminating Thread will always wake up all Threads waiting for it. Just throw in another Thread.sleep() at the end of Calculator.run() and you'll see.

Update

There's a crucial difference I just realized reading John's answer.

The misunderstanding resides in the phrase 'waiting for it'. A Thread will indeed notify all waiters, but it has nothing to do with the Thread concept.

In fact it is a special behaviour, which is in particular, that a Thread, whenever it reaches the end of it's lifespan, will notify all waiters that are waiting on the Thread object itself. As John already pointed out, this will happen at some point 'after' Thread.exit(), hence inside the JVM, thus there is no relation to object deallocation.

Conclusion

Although this is most probably the reason for the described error, one should never rely on anything to happen inside the JVM. The fact this behaviour is that fairly documented is a definite indicator.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=454052&siteId=1