'Understanding Wait in Thread Java

I have the following code:-

class ThreadB extends Thread {
    int total;

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

public class Solution {
    public static void main(String[] args) throws InterruptedException {
        ThreadB b = new ThreadB();
        b.start();
        synchronized (b){
            b.wait();
        }
        System.out.println(b.total);
    }
}

Whenever I run this I get my output as 10. If I comment the wait line I get the output as 0 always.

I am confused as to why do I get my answer as 10 always. There are 2 threads , ThreadB and main thread so when I execute the wait method then ThreadB should be waiting as per definition and values should not get added and hence 0 should be printed by main thread then?



Solution 1:[1]

Every object has an intrinsic lock that threads can acquire using synchronized. A thread calls wait when there isn't anything it can do until something changes (for instance, it might be trying to insert into a bounded queue that is currently full), it calls wait on the object whose lock it acquired with synchronized. When the main thread calls b.wait(), it means the main thread is the one that is going dormant.

When the code has wait commented out the ThreadB thread is still in the process of starting and the main thread can take the lock, then release the lock and print total before ThreadB can acquire the lock, at which time total is still 0. Technically there is a race and it's not guaranteed which thread goes first but the main thread has a good head start.

When the code uses wait then the main thread acquires the lock on ThreadB (again getting there ahead of ThreadB), then waits until it receives a notify. It happens that when a thread terminates it causes the scheduler to notify any threads in its waitset, so when threadB finishes it causes the main thread to wake up and proceed from there. Since ThreadB is done total is 10 by the time the main thread gets around to printing it.

If somehow the main thread did not get the lock before ThreadB, then (since ThreadB holds onto the lock for the whole time it is running) it couldn't acquire the lock until after ThreadB was finished. That would mean it wasn't in the waitset at the time the dying thread sent its notification, but would wait later, and there wouldn't be any notification to wake it up and it would hang.

This kind of problem - races that result in lost notifications and threads hanging - can happen when wait/notify is misused. For the right way to use wait and notify read https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html.

If you specifically want a thread to wait for another thread to finish, Thread has an instance method called join that is used for that, which the main thread would call here like

b.join();

BTW the notify-on-termination behavior is documented in the api doc for java.lang.Thread#join, it says:

This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

Note the warning against synchronizing on Thread objects. It doesn't play nice with JDK code like join that is locking on threads.

Solution 2:[2]

I am confused as to why do I get my answer as 10 always. There are 2 threads , ThreadB and main thread so when I execute the wait method then ThreadB should be waiting as per definition and values should not get added and hence 0 should be printed by main thread then?

You are getting a result because of the way Java threads terminate. When a Thread finishes, the Thread object itself is notified. So your wait() method causes the thread to wait for the termination of the background thread. The join() method is implemented by a call to wait() and you should use join() directly and not wait().

If I comment the wait line I get the output as 0 always.

If you don't have the wait() line then most likely the main thread will finish and get the value of b.total before the b thread is even started. Starting threads take a little bit of time and you need to have b.join() to make sure that you wait for the b thread to finish and to synchronize with any memory changes that the other thread has made, in this case to b.total.

If you put a sleep instead of the wait, you might still see the value of 0 even if the b thread had already set it to 10 because there is nothing that is synchronizing the memory and the b.total value of 0 might be cached. The join() method waits for the thread to finish and synchronized memory so that you can see the results of the thread.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2