Reputation: 79
I was trying to use java.lang.Object.wait()
method and have written 3 different sample codes wherein I am getting different behavior of wait()
method.
sample 1)
class Main {
public static void main(String[] args) throws InterruptedException {
ThreadB b = new ThreadB();
b.start();
Thread.sleep(10000);
synchronized (b) {
System.out.println("main thread trying to call wait() method"); //--> 3
b.wait();
System.out.println("main thread got notification");
System.out.println(b.total);
}
}
}
class ThreadB extends Thread {
int total = 0;
public void run() {
synchronized (this) {
System.out.println("child thread starts calculation"); //--> 1
for (int i=0; i<=100; i++) {
total = total + i;
}
System.out.println("child thread trying to give notification"); //--> 2
this.notify();
}
}
}
sample 2)
public class Main{
public static void main (String[] args) throws InterruptedException {
Thread t = new Thread();
t.start();
System.out.println("X"); //--> 1
synchronized(t) {
System.out.println("starting to wait"); //--> 2
t.wait(10000);
System.out.println("waiting on t"); //--> 3
}
System.out.println("Y"); //--> 4
}
}
sample 3)
public class Main{
public static void main (String[] args) throws InterruptedException {
Thread t = new Thread() {public void run()
{System.out.println("I am the second thread.");}};
t.start();
System.out.println("X"); //--> 1
synchronized(t) {
Thread.sleep(4000);
System.out.println("starting to wait"); //--> 2
t.wait(10000);
System.out.println("waiting on t"); //--> 3
}
System.out.println("Y"); //--> 4
}
}
In sample 1)
main thread goes in waiting state forever as it has called b.wait()
method and there is no thread to provide notify()
or notifyAll()
on object b
. There was child thread that has already been terminated before main thread called b.wait()
method.
This output is what I expected.
In sample 2)
main thread goes in waiting state for 10 seconds (t.wait(10000);
) after printing
X
starting to wait
after 10 seconds main thread executes
waiting on t
Y
This is also my expected output.
In sample 3)
main thread is NOT going in waiting state (t.wait(10000);
) even though it is sure that child thread would have been terminated by the time main thread called t.wait(10000);
So why it didn't wait ? and straightaway executed
starting to wait
waiting on t
Y
This is NOT my expected output.
Upvotes: 2
Views: 1069
Reputation: 96434
For the first two examples your expectations seem correct. In the third example it seems reasonable to expect that t will finish before the main thread starts waiting, and then the main thread will hang until it times out.
But as you observed, that isn't what happens.
A waiting thread doesn't stop waiting unless interrupted or notified (except for spurious wake ups, but those are the result of unpredictable race conditions; the behavior in the posted code happens reliably, so I think spurious wakeups can be excluded here).
Since there is nothing interrupting the main thread, and its wait is cut short and we've ruled out spurious wakeups, it must be receiving a notification. There is only one thing that can provide the notification, and that is the t thread.
For t to notify the main thread it must have been alive at the time that t started waiting. So what is keeping it around?
There is some not-well-known behavior that occurs when a thread terminates. The API documentation for Thread.join 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.
What happens is:
1) t prints its output and is on the way out of its run method.
2) There is a race between t and the main thread to acquire the lock on t. t needs it to call notifyAll, main needs it to enter the synchronized block. The main thread happens to grab the lock first.
3) t hangs around until it can acquire the lock.
4) The main thread enters the wait method (releasing the lock).
5) The t gets the lock and calls t.notifyAll.
5) The main thread is notified and leaves the wait method (reacquiring the lock).
Some lessons:
Don't synchronize on threads (this is a good example of why the API docs say not to do this, here you inadvertently delayed a thread from dying in a timely fashion).
If a thread isn't waiting, it doesn't get notified. If a thread starts waiting after the notification has already happened, that notification is lost.
Don't rely solely on notifications (it makes your code vulnerable to race conditions), instead use notifications along with some condition that the other thread can set. Call wait in a loop with a test condition. If you see the Thread.join source code, it is a good example, it looks something like:
while (isAlive()) {
wait(0);
}
Don't sleep while holding a lock. It makes the system less responsive for no benefit.
Be very careful about making assumptions about the order things happen in.
Upvotes: 1