7

I wrote a test app that should never stop. It issues t.wait() (t is a Thread object), but I never call notify. Why does this code end? Despite the main thread synchronizing on t, the spawned thread runs, so it doesn't lock this object.

public class ThreadWait {
    public static void main(String sArgs[]) throws InterruptedException {
        System.out.println("hello");
        Thread t = new MyThread();
        synchronized (t) {
            t.start();
            Thread.sleep(5000);
            t.wait();
            java.lang.System.out.println("main done");
        }
    }
}

class MyThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            java.lang.System.out.println("" + i);
            try {
                Thread.sleep(500);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

The result is that the main thread waits 5 seconds and during this time worker gives its output. Then after 5 seconds are finished, the program exits. t.wait() does not wait. If the main thread wouldn't sleep for 5 seconds (commenting this line), then t.wait() would actually wait until the worker finishes. Of course, join() is a method to use here, but, unexpectedly, wait() does the same thing as join(). Why?

Maybe the JVM sees that, since only one thread is running, there is no chance to notify the main thread and solves the deadlock. If this is true, is it a documented feature?

I'm testing on Windows XP, Java 6.

bcsb1001
  • 2,834
  • 3
  • 24
  • 35
Jarekczek
  • 7,456
  • 3
  • 46
  • 66
  • What happens when you sleep only for 1 or 2 seconds? Then I bet you'll have the main thread wait for thread t to finish running... – Renato Jan 27 '12 at 08:20

2 Answers2

11

You're waiting on a Thread - and while most objects aren't implicitly notified, a Thread object is notified when the thread terminates. It's documented somewhere (I'm looking for it...) that you should not use wait/notify on Thread objects, as that's done internally.

This is a good example of why it's best practice to use a "private" object for synchronization (and wait/notify) - something which only your code knows about. I usually use something like:

private final Object lock = new Object();

(In general, however, it's cleaner to use some of the higher-level abstractions provided by java.util.concurrent if you can. As noted in comments, it's also a good idea to implement Runnable rather than extending Thread yourself.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • A number of method in Thread are synchronized on that object and use wait/notify, so spurious wake ups should be expected. Its also good practice not to extend Thread but to use a Runnable which is wrapped by a Thread. This would also avoid this issue. – Peter Lawrey Jan 27 '12 at 08:31
  • 1
    @PeterLawrey: So long as the OP then used wait/notify on the runnable, not on the thread :) I still prefer to have a "dumb" object which I don't expose *anywhere* else, personally. – Jon Skeet Jan 27 '12 at 08:34
  • Ideally, you want both as you don't know how the code might be changed in the future. IMHO having multiple strategies which are best practice protects your code better. – Peter Lawrey Jan 27 '12 at 08:38
  • @PeterLawrey: Not sure which "both" you're talking about. I certainly agree that implementing Runnable is cleaner than extending Thread. – Jon Skeet Jan 27 '12 at 09:00
  • And using a lock which is encapsulated is also a good idea. either would solve the problem, both is better. – Peter Lawrey Jan 27 '12 at 09:02
  • 1
    @PeterLawrey: Right, I'm with you. Totally agreed :) – Jon Skeet Jan 27 '12 at 09:04
  • Jon, you are right. Locking on another object, even public, mades the app infinite, which was the expected behaviour in this case :) If one finds the docs Jon is missing at the moment, please post a comment. – Jarekczek Jan 27 '12 at 16:22
  • Did you find the documentation? I remember reading this somewhere but can't find it spelled out anywhere. – Joni Jan 27 '12 at 18:59
  • Notifying the Thread object is not documented. It's just the way Thread.join() is implemented. Found it at [coderanch](http://www.coderanch.com/t/557517/java-programmer-SCJP/certification/waiting-thread-notified-without-explicit#2530455). Similar question (a duplicate of mine but with a better title), also [here](http://stackoverflow.com/questions/9866193/who-and-when-notify-the-thread-wait-when-thread-join-is-called). – Jarekczek May 23 '12 at 11:03
  • @jarekczek: I'm sure it *is* documented - I've seen the documentation, I'm positive - but it's not documented in the obvious location. – Jon Skeet May 23 '12 at 11:18
  • 2
    @Jon, [Malcolm](http://stackoverflow.com/users/45668/malcolm) [found](http://stackoverflow.com/a/16955524/772981) it in [Java 7 Thread.join documentation](http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join%28long%29). – Jarekczek Jun 06 '13 at 06:58
8

The JavaDoc for wait gives the answer: spurious wakeups are possible. This means the JVM is free to end a call to wait whenever it wants.

The documentation even gives you a solution if you don't want this (which is probably always the case): put the call to wait in a loop and check whether the condition you wait for has become true after every wakeup.

Philipp Wendler
  • 11,184
  • 7
  • 52
  • 87
  • 3
    @Boris In case of a spurious wakeup, there is no exception. An `InterruptedException` is thrown only when the thread is interrupted. – Philipp Wendler Jan 27 '12 at 08:19
  • 2
    I doubt that this is *actually* a spurious wakeup - I believe it's the behaviour of waiting on `Thread` in particular; it *will* be notified when the thread terminates. It's not spurious in the normal sense. – Jon Skeet Jan 27 '12 at 08:28
  • 2
    Even without spurious wakeups, with the monitor pattern (or any of its variants) you always want to loop and check for conditions. That prevents problems if you later add another condition (java and C# monitors use one object as both the lock and the condition variable, so you cannot use one condition variable per condition). It also prevents you from proceding if the condition was met, but another thread waiting on the same object went first, and now the condition is no longer true. – Kevin Cathcart Jan 27 '12 at 16:57