Running wait() on a Thread instance from within main() in Java
Asked Answered
P

2

8

I am playing around with the timed version of wait() in java.lang.Object and have observed that it acts differently in two different scenarios.

Scenario1: Using the default definition of run() in Thread

public static void main (String[] args) throws InterruptedException {
    Thread t = new Thread();    
    t.start();
    System.out.print("X");
    synchronized(t) { t.wait(10000);}
    System.out.print("Y");
}

Questions on scenario1: I am experiencing a delay between X and Y. Is this because I am calling wait() from main (even though, on t) and therefore the call stack of the main thread is being used, rather than that of the second thread?

Scenario2: Subclassing Thread on-the-fly to override run() in order to print something.

public static void main (String[] args) throws InterruptedException {
     Thread t = new Thread() {public void run() 
                     {System.out.print("I am the second thread.");}};
     t.start();
     System.out.print("X");
     synchronized(t) { t.wait(10000);}
     System.out.print("Y");
}

Questions on scenario2: I am NOT experiencing any delay at all! What has changed just because I have overridden run()? Now, each time I run the program it immediately prints "XI am the second thread.Y" without any delay, whatsoever! Where has the effect of wait() gone?

Phonate answered 2/6, 2015 at 13:8 Comment(7)
Why would you use "the default implementation of run" in a thread?Pegram
@Patrick: only to test threading in Java, nothing else.Phonate
I cannot reproduce your findings with scenario 2 as described. There is something going on in your environment, or with the enclosing class.Messier
I am not sure what you are trying to do, but there are fundamental issues with how you are using wait. Are you expecting thread to sleep when calling t.wait()?Piliferous
I would guess that in the second case t.wait() is executed before the thread itself is started and in the first case the thread is not started at all, so there is nothing to be executed, so main thread is just waiting for itself.Seymore
@jdv: I am using ideone.com online compilerPhonate
@softwarelover, yeah this is a case of undefined behaviour as described in the ControlAltDel's answer.Messier
P
1

The explanation about how the thread finishing sends a notifyAll is relevant and correct, +1 from me. I'll try to add some information about why this is relevant.

When you call

synchronized(t) { t.wait(10000);}

in the main thread, it is the main thread that does the waiting. t is the monitor that the main thread is waiting on. Your expectation that this should make your t thread go dormant is mistaken.

The monitor here (the shared object being locked on, which happens to be t) is used to communicate between the different threads, a thread calls notifyAll on the monitor and the other threads waiting on the monitor receive the notification. You can think of the monitor as a shared communication point.

In your first example, the thread t starts and finishes immediately (because it doesn't have anything to do). The thread finishes and sends its notification before the main thread starts waiting, so you see a delay until the wait times out.

In the second example, the thread t has something to print, there's a race condition between it and the main thread. It's a free-for-all, what happens first depends on accidents of timing. What you're seeing is that the thread t now has to print a line to the console, so it manages to keep busy long enough that it's still alive at the time the main thread starts to wait, allowing the main thread to receive the notification when t finishes, causing the main thread to cut its wait short.

Pinsk answered 2/6, 2015 at 13:21 Comment(11)
Actually, I am preparing for OCP Java 7 examination and I just came across a sample problem that led me to writing this post. I have no intention of using join() or anything like that. The sample question just gives scenario1 with default run() and asks to pick an answer (D is correct) from below: A. It prints X and exits. B. It prints X and never exits. C. It prints XY and exits almost immediately. D. It prints XY with a 10-second delay between X and Y. E. It prints XY with a 10000-second delay between X and Y. F. The code does not compile. G. An exception is thrown at runtime.Phonate
@softwarelover: that seems like an error, D is not correct.Pinsk
Scenario1, that is. I am using ideone.com online compiler.Phonate
Pls allow me to ask you something fundamental. Is this how a thread launched from the main thread, dies? You mentioned notify(), but nowhere had I put notify() in my code.Phonate
@softwarelover: The notification is done by the java libraries, it happens whenever a thread terminates, without your code doing anything. only threads waiting on that thread (using that thread as a monitor, the thing ControlAltDel and James Large are saying not to do, as bad style) will see the notification.Pinsk
It is not uncommon to extend Thread and write synchronized methods in the subclass. Then those methods automatically use "this" as the monitor, which is a Thread instance. Also, if threads send notifications shortly before dying why do people put notify, notifyAll within a synchronized block? Isn't it then redundant?Phonate
@softwarelover: i suspect you're looking at toy examples and maybe just bad code. a lot of toy examples are just confusing. you don't synchronize on thread objects but on data structures or dedicated locks. typically synchronizing belongs in the data structure being accessed, not in the thread.Pinsk
Aha! Got it. That must be it. Yes, I am reading "what happens if we do that" types of questions. One last question on this. Suppose I create two threads A, B and have them wait() and notify() on some object called x. Ideally, I would have synchronized(x) {x.wait();} in A and synchronized(x) {x.notify();} in B. What would happen if I removed x.notify(); from thread B. Would thread A be forever waiting?Phonate
@softwarelover: yes, probably. however, there is a technical caveat where it's possible a thread can stop waiting without having been notified (the "spurious wakeup"). the recommendation is to use a loop with a condition variable whenever you wait, see docs.oracle.com/javase/tutorial/essential/concurrency/….Pinsk
Yes Nathan, I am aware of spurious wakeup's. I am just confused by one thing. I knew that a thread waiting on a lock must be notified by another thread by invoking notify() on that lock, so that the waiting thread becomes runnable. But, earlier, you said about the code in my original posting that the dying thread automatically sends notification to the "waiting" main thread. I am trying to distinguish the two. Please help me out. :(Phonate
@softwarelover: the difference is what your code is using as a lock. The jvm code is implementing Thread#join using threads as locks. if you're not waiting on a lock you don't get notifications for that lock. for examples maybe try looking at JCIP more and OCP exam questions less. but if you want "what happens if we do that" answers I have a lot of those posted.Pinsk
A
7

You have actually run into exactly why you should NEVER call wait or notify(All) on Thread (see the JavaDocs for Thread). Internally, Thread uses wait and notifyAll to implement Thread.join(), so what's happening in the second case is you thread enters wait, but then the other thread dies and calls notifyAll(), which wakes up your main thread.

Use Thread.sleep if you just want to wait for an elapsed time, use Thread.join if you actually want to wait for the thread’s termination. Also, read the javadocs in Object for proper usage of wait, notify, and notifyAll.

javaDoc:

public final void join(long millis)
                throws InterruptedException

Waits at most millis milliseconds for this thread to die. A timeout of 0 means to wait forever. 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.

Allogamy answered 2/6, 2015 at 13:14 Comment(3)
It should be noted that this depends on an unpredictable timing. If the thread is already dead at the time, wait is invoked, there is no further notification and hence, the caller will wait the full time. So if waiting for the termination is intended, join should be used.Hehre
Never is a long time. You can (and people do) write a program that calls someThread.wait() and someThread.notify(), and as long as the program uses the cannonical wait()/notify() pattern , then it will work correctly even despite the fact that the Thread class also wait()s and notify()s the same object. It is however pointless, and bad style, and speaking as a member of a large software development team, I would not allow any of my peers to check in code that did that.Coonskin
@jameslarge yes, you are right - it could be used correctly, and of course there are times when you want/need to use thread monitors to properly synchronize. But in all situations I've ever encountered, you can perfectly well use another object (like a plan Object instance) for the monitor without adding in unexpected behaviour in how Thread uses this.Allogamy
P
1

The explanation about how the thread finishing sends a notifyAll is relevant and correct, +1 from me. I'll try to add some information about why this is relevant.

When you call

synchronized(t) { t.wait(10000);}

in the main thread, it is the main thread that does the waiting. t is the monitor that the main thread is waiting on. Your expectation that this should make your t thread go dormant is mistaken.

The monitor here (the shared object being locked on, which happens to be t) is used to communicate between the different threads, a thread calls notifyAll on the monitor and the other threads waiting on the monitor receive the notification. You can think of the monitor as a shared communication point.

In your first example, the thread t starts and finishes immediately (because it doesn't have anything to do). The thread finishes and sends its notification before the main thread starts waiting, so you see a delay until the wait times out.

In the second example, the thread t has something to print, there's a race condition between it and the main thread. It's a free-for-all, what happens first depends on accidents of timing. What you're seeing is that the thread t now has to print a line to the console, so it manages to keep busy long enough that it's still alive at the time the main thread starts to wait, allowing the main thread to receive the notification when t finishes, causing the main thread to cut its wait short.

Pinsk answered 2/6, 2015 at 13:21 Comment(11)
Actually, I am preparing for OCP Java 7 examination and I just came across a sample problem that led me to writing this post. I have no intention of using join() or anything like that. The sample question just gives scenario1 with default run() and asks to pick an answer (D is correct) from below: A. It prints X and exits. B. It prints X and never exits. C. It prints XY and exits almost immediately. D. It prints XY with a 10-second delay between X and Y. E. It prints XY with a 10000-second delay between X and Y. F. The code does not compile. G. An exception is thrown at runtime.Phonate
@softwarelover: that seems like an error, D is not correct.Pinsk
Scenario1, that is. I am using ideone.com online compiler.Phonate
Pls allow me to ask you something fundamental. Is this how a thread launched from the main thread, dies? You mentioned notify(), but nowhere had I put notify() in my code.Phonate
@softwarelover: The notification is done by the java libraries, it happens whenever a thread terminates, without your code doing anything. only threads waiting on that thread (using that thread as a monitor, the thing ControlAltDel and James Large are saying not to do, as bad style) will see the notification.Pinsk
It is not uncommon to extend Thread and write synchronized methods in the subclass. Then those methods automatically use "this" as the monitor, which is a Thread instance. Also, if threads send notifications shortly before dying why do people put notify, notifyAll within a synchronized block? Isn't it then redundant?Phonate
@softwarelover: i suspect you're looking at toy examples and maybe just bad code. a lot of toy examples are just confusing. you don't synchronize on thread objects but on data structures or dedicated locks. typically synchronizing belongs in the data structure being accessed, not in the thread.Pinsk
Aha! Got it. That must be it. Yes, I am reading "what happens if we do that" types of questions. One last question on this. Suppose I create two threads A, B and have them wait() and notify() on some object called x. Ideally, I would have synchronized(x) {x.wait();} in A and synchronized(x) {x.notify();} in B. What would happen if I removed x.notify(); from thread B. Would thread A be forever waiting?Phonate
@softwarelover: yes, probably. however, there is a technical caveat where it's possible a thread can stop waiting without having been notified (the "spurious wakeup"). the recommendation is to use a loop with a condition variable whenever you wait, see docs.oracle.com/javase/tutorial/essential/concurrency/….Pinsk
Yes Nathan, I am aware of spurious wakeup's. I am just confused by one thing. I knew that a thread waiting on a lock must be notified by another thread by invoking notify() on that lock, so that the waiting thread becomes runnable. But, earlier, you said about the code in my original posting that the dying thread automatically sends notification to the "waiting" main thread. I am trying to distinguish the two. Please help me out. :(Phonate
@softwarelover: the difference is what your code is using as a lock. The jvm code is implementing Thread#join using threads as locks. if you're not waiting on a lock you don't get notifications for that lock. for examples maybe try looking at JCIP more and OCP exam questions less. but if you want "what happens if we do that" answers I have a lot of those posted.Pinsk

© 2022 - 2024 — McMap. All rights reserved.