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

如果在线程上调用java wait(),则该方法也会在run()方法终止时退出,java,multithreading,Java,Multithreading,我对wait()方法的一个特殊用例感到困惑。 根据javadoc,当发生以下情况之一时,等待应结束: 另一个线程调用notify或notifyAll(好的,关于notify的详细信息,请参阅javadoc,但这与这个问题无关) 另一个线程中断此(等待)线程 超时过期(如果使用带超时的等待版本) 在我等待的对象本身是线程的情况下,即使没有调用notify(),wait()也会退出,并且上述条件都不成立。 但是当Thread.run()方法结束时,就会发生这种情况。 虽然这种行为可能有道理,但它

我对wait()方法的一个特殊用例感到困惑。
根据javadoc,当发生以下情况之一时,等待应结束:

  • 另一个线程调用notify或notifyAll(好的,关于notify的详细信息,请参阅javadoc,但这与这个问题无关)
  • 另一个线程中断此(等待)线程
  • 超时过期(如果使用带超时的等待版本)
在我等待的对象本身是线程的情况下,即使没有调用notify(),wait()也会退出,并且上述条件都不成立。 但是当Thread.run()方法结束时,就会发生这种情况。 虽然这种行为可能有道理,但它不应该记录在线程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!");
    }

}

自Java 7以来,在以下文件中对其进行了记录:

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


直接扩展线程是一种常见的反模式。问题是,由于Thread是一个复杂的类,您可能会产生各种意外的后果。它所做的一件事是在线程停止时通知尝试连接()的线程。这会通知等待该线程对象的任何线程

一个常见的Java难题是错过使用扩展线程的类


顺便说一句,虽然使用wait()/notify()已经过时9年多了,但在极少数情况下仍然可以使用它们。如果您这样做,您应该在notify()块中更改某些内容的状态,并在等待()时等待该状态更改。这是因为如果在等待之前调用notify,它将丢失,并且wait()可能会虚假存在,即使您不扩展线程。

如果您想要可预测和可控的等待/通知行为,请在线程和等待它的方法之间共享一个对象。

您如何知道
notify
没有被调用?您假设,因为您没有调用
notifyAll
,所以它不可能被调用。但这是一个完全虚假的假设。因为如果它被调用,它应该被记录下来,因为它将是类契约的一个非常重要的部分。如果我不能依赖实现者(在本例中是自动激活的)声明在非常特定的情况下它将调用notify(),那么,除了我创建的类之外,我永远不会信任任何类。对于一个您无法控制其实现的对象,您不能依赖wait/notify的语义,因为该实现可能需要等待一些事情,并因为一些事情而通知。显然,Sun使用synchronized(thread)来实现thread.join()是一个错误。我个人见过很多让人感到困惑的案例。Sun/甲骨文公司肯定收到了很多关于此事的抱怨,所以他们最终记了下来。但是thread.join()并不是一个好地方,所有的官方文档都将扩展thread作为一种可行的方法。我会考虑你的建议,但我不知道这是否真的是一种反模式,还是更有用的预防措施。当你谈论“旧式学校”时,它是非常有趣的。在新学校通知的首选是什么?@AgostinoX您应该更喜欢。新学校=。特别是,它包含几种实现通知的方法,例如CountDownLatch、CyclicBarrier、Exchanger等。当您希望跨线程交换消息时,它还包含所有阻塞队列。而且它有几个ExecutorService的实现,所以在很多情况下,您实际上不需要手动启动线程。因此,看起来他们已经修复了文档中的一个疏忽,但实际上它也应该在java 1.6中进行记录。我检查过了,它没有写出来,行为也一样。@AgostinoX:线程也可以在不被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这种情况在实践中很少发生,但应用程序必须通过测试本应导致线程被唤醒的条件来防范它,并在条件不满足时继续等待。”。因此,实际发生的情况符合
wait
的规范。