这不是虚假唤醒,虚假唤醒是由 JVM 中的竞争条件引起的。这是代码中的竞争条件。
println 使线程 1 保持活动状态足够长的时间,以便线程 2 可以在线程 1 终止之前开始等待。
一旦 thread1 终止,它就会向其监视器上等待的所有内容发送通知。 thread2收到通知并停止等待。
删除 println 会减少线程 1 完成所需的时间,以便在线程 2 可以开始等待线程 1 时线程 1 已经完成。 thread1 不再活动,并且在 thread2 开始等待之前它的通知已经发生,因此 thread2 永远等待。
线程在死亡时发送通知记录在Thread#join 的 API http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join-long-:
此实现使用以 this.isAlive 为条件的 this.wait 调用循环。当线程终止时,将调用 this.notifyAll 方法。建议应用程序不要在 Thread 实例上使用 wait、notify 或 notifyAll。
(对于调用notifyAll的线程来说,它必须持有锁,如果另一个线程抢占了锁,它可以使终止线程保持活动状态并延迟notifyAll,直到终止线程可以获得锁。)
道德(好吧,道德之一)是始终在带有条件变量的循环中等待,请参阅Oracle 教程 http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html。如果将 Thread2 更改为如下所示:
Thread t2 = new Thread() {
public void run()
{
try {
synchronized (t1) {
while (t1.isAlive()) {
t1.wait();
}
}
} catch (InterruptedException e) {
return;
}
System.out.println("Done.");
}
};
那么无论线程 2 是否可以在线程 1 完成之前开始等待,线程 2 都应该退出。
当然,这是完整的玩具示例领域: