2

I'm puzzled with a particular use case of the wait() method.
According with javadoc, wait should end when one of the following condition happens:

  • another thread invokes notify or notifyAll (ok, see javadoc for a detail on notify but this is not relevant for this question)
  • another thread interrupts this (waiting) thread
  • a timeout expires (if using the wait version with timeout)

In the case in which the object i'm waiting on is itself a Thread, it happens that the wait() exits even if no notify() has been called, and no one of the above conditions holds. But it happens when the Thread.run() method ends. While this behavior may make sense, shouldn't it be documented inside of Thread javadoc? I find it very confusing also because it overlaps with join() behavior.

This is my test code:

public static class WorkerThread extends Thread {

    @Override public void run() {

        try{
           System.out.println("WT: waiting 4 seconds");
           Thread.sleep(4000);

           synchronized (this) {
            notify();
           }

           System.out.println("WT: waiting for 4 seconds again");
           Thread.sleep(4000);
           System.out.println("WT: exiting");

        } catch (InterruptedException ignore) {
            ignore.printStackTrace();
        }

    }

}

public static void main (String [] args) throws InterruptedException {

    WorkerThread w = new WorkerThread();

    w.start();

    synchronized(w) {
        w.wait();
        System.out.println("MT: The object has been notified by the thread!");
    }

    synchronized(w) {

        w.wait(); //THIS FINISHES WITHOUT notify(), interrupt() OR TIMEOUT!

        System.out.println("MT: The thread has been notified again!");
    }

}
AgostinoX
  • 7,477
  • 20
  • 77
  • 137
  • How do you know `notify` wasn't called? You're assuming that because you didn't call `notifyAll`, it can't have been called. But that's a completely bogus assumption. – David Schwartz Jun 02 '13 at 21:45
  • because if it got called, it should have been documented since it would be a very important part of the contract of the class. if i cannot rely on the fact that the implementor (an autoritative one, in this case) declares that in a very specific case it will call notify(), then i would never trust any class but the ones that i create. – AgostinoX Jun 02 '13 at 21:56
  • 1
    You cannot rely on the semantics of wait/notify for an object whose implementation you don't control because the implementation may need to wait for things and notify because of things. – David Schwartz Jun 02 '13 at 22:03
  • obviously, it was a mistake on Sun's part to use synchronized(thread) to implement thread.join(). I've personally seen multiple cases where it got people really puzzled. Sun/Oracle must have received lots of bitching about it so they finally made a note of it. but thread.join() isn't really a good place for that note. – ZhongYu Jun 03 '13 at 02:14

3 Answers3

7

It is documented since Java 7, in the documentation of the join() method:

As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • so it looks more like they have fixed an oversight in documentation, but actually it should have documented in java 1.6 as well. I checked, it isn't written and the behavior is the same. – AgostinoX Jun 02 '13 at 22:26
  • 3
    @AgostinoX [Even in prior versions, the javadoc for wait says](http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait%28long%29): "*A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied.*". So what happened is actually compliant with `wait`'s specification. – assylias Jun 02 '13 at 23:09
5

It is a common anti-pattern to extend Thread directly. The problem is you can have all sorts of unintended consequences as Thread is a complex class. One thing it does is notify threads trying to join() the thread when it stops. This has the effect of notifying any thread waiting on that thread object.

A common Java puzzler is to miss use a class which extends Thread.

BTW While using wait()/notify() has been old school for more than 9 years now, you can still use them in rare situations. If you do you should change the state of something in the notify() block and wait for that state change when you wait(). This is because if you call notify before wait, it is lost and wait() can exist spuriously, even if you don't extend thread.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • All the official documentation presents extending Thread as a feasible way. I will consider your advice, but i don't know if this is really an anti-pattern or it is more a useful precaution. It's very interesting when you talk about the "old school". What is the first choice for notifications in the new school? – AgostinoX Jun 02 '13 at 22:01
  • 2
    @AgostinoX You should prefer [implementing Runnable vs. extending Thread](http://stackoverflow.com/questions/541487/implements-runnable-vs-extends-thread). New school = [java.util.concurrent](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html). In particular, it contains several ways to achieve notification, e.g. CountDownLatch, CyclicBarrier, Exchanger etc. + all the blocking queue when you want to exchange messages across threads. And it has several implementations of ExecutorService so you don't really need to start threads manually in many situations. – assylias Jun 02 '13 at 23:13
1

If you want predictable and controllable wait/notify behaviour, share an Object between the thread and the method waiting on it.

Score_Under
  • 1,189
  • 10
  • 20