Running wait() on a Thread instance from within ma

2019-02-21 17:08发布

问题:

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?

回答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.



回答2:

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.