2

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?

4
  • b.wai() doesn't mean that thread b will wait, but the thread that is calling the waitmethod. The calling thread (here it's the main thread) will wait for a notification from thread b. Commented Feb 24, 2022 at 4:54
  • You really shouldn't call wait() on instances of Thread, nor call synchronized on Thread. It can cause deadlocks. Commented Feb 24, 2022 at 17:14
  • See the javadoc for Object.wait() and the Guarded Blocks tutorial to learn what b.wait() actually means and, for how the wait(), notify(), and notifyAll() methods are supposed to be used. Commented Feb 25, 2022 at 3:40
  • Related to: stackoverflow.com/questions/17840397/… Commented Jun 13, 2024 at 18:18

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Great explanation, good and detailed answers get overlooked too often. I wonder if the notify() when a thread exits is an implementation detail that you can't depend on, or whether it is guaranteed by the documentation/specification.
@ErwinBolwidt: thanks, added quote of api doc describing the notify-on-exit.
@ErwinBolwidt, Oracle documents that notifyAll() will be called on every Thread instance when the thread terminates, but then in the very next sentence, they advise developers to never call wait(), notify(), or notifyAll() on any Thread instance. I'm guessing that they would like to formally deprecate the practice, but they haven't figured out how to do that when the same inherited method is not deprecated for other classes.
0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.