如果在Thread上调用java wait(),则该方法也会在run()方法终止时退出

Ago*_*noX 2 java multithreading

我对wait()方法的特定用例感到困惑.
根据javadoc,当出现下列情况之一时,wait应该结束:

  • 另一个线程调用notify或notifyAll(好的,请参阅javadoc以获取有关通知的详细信息,但这与此问题无关)
  • 另一个线程中断这个(等待)线程
  • 超时到期(如果使用带超时的等待版本)

在我正在等待的对象本身就是一个Thread的情况下,即使没有调用notify(),并且没有上述条件之一成立,也会发生wait()退出.但是当Thread.run()方法结束时会发生这种情况.虽然这种行为可能有意义,但不应该在Thread javadoc中记录吗?我发现它也很混乱,因为它与join()行为重叠.

这是我的测试代码:

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!");
    }

}
Run Code Online (Sandbox Code Playgroud)

JB *_*zet 7

自Java 7以来,在join()方法文档中记录了它:

当一个线程终止时,将调用this.notifyAll方法.建议应用程序不要在Thread实例上使用wait,notify或notifyAll.

  • @AgostinoX [即使在以前的版本中,等待的javadoc也是如此](http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait%28long%29):"*线程也可以在没有被通知,中断或超时的情况下唤醒,即所谓的虚假唤醒.虽然这在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防范它,如果条件不满意,继续等待.*".所以发生的事情实际上符合`wait`的规范. (3认同)

Pet*_*rey 5

直接扩展Thread是一种常见的反模式.问题是你可以有各种意想不到的后果,因为Thread是一个复杂的类.它做的一件事是通知线程在停止时试图加入()线程.这具有通知等待该线程对象的任何线程的效果.

一个常见的Java益智游戏是错过使用扩展Thread的类.

BTW虽然使用wait()/ notify()已经老了9年多了,但你仍然可以在极少数情况下使用它们.如果这样做,您应该更改notify()块中某些内容的状态,并在wait()时等待该状态更改.这是因为如果你在等待之前调用notify,它就会丢失并且wait()可能会伪造,即使你没有扩展线程.

  • @AgostinoX您应该更喜欢[实现Runnable与扩展线程](http://stackoverflow.com/questions/541487/implements-runnable-vs-extends-thread).新学校= [java.util.concurrent](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html).特别是,它包含几种实现通知的方法,例如CountDownLatch,CyclicBarrier,Exchanger等.当您想要跨线程交换消息时,所有阻塞队列.它有几个ExecutorService实现,因此您不需要在许多情况下手动启动线程. (2认同)