我有这段代码:
public class Nit extends Thread {
public void run() {
try {
synchronized(this) {
this.wait();
}
System.out.println("AAA");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Nit n = new Nit();
n.start();
synchronized(n) {
n.notify();
}
}
}
当我从cmd运行它时,它永远不会退出,就像它是一个无限循环。我不明白为什么。我唯一能想到的是Nit n
还在等待,但我不明白为什么?
答案 0 :(得分:4)
您正在观察种族条件。在等待之前通知。因此,等待坐在那里,永远等待。
如果你经常调用这个代码,你可能会看到它有时会传递 - 当新线程比主线程更快时。使示例有效的一种方法是:在调用Thread.sleep(1000)
之前尝试添加对notify()
左右的调用。或者,即使是主线程上的println()调用(之前的 notify()
也可能会改变时间)。
除此之外:这些微妙之处是你实际上避免使用"低级别"的主要原因。原语如wait / notify。相反,您使用标准API必须提供的强大抽象(如队列)。
答案 1 :(得分:1)
notify方法告诉调度程序选择要通知的线程,只选择那些当前正在等待调用notify的同一个锁的线程。
在这种情况下,n线程直到通知已经发生之后才开始等待,因此没有任何东西可以将线程从等待中唤醒。您可能假设等待线程将在它们开始等待之前看到通知,或者JVM必须在主线程经过调用开始之前给出n个线程CPU时间,但这些假设无效。
引入条件标志作为Nit的实例成员:
public class Nit extends Thread {
boolean notified = false;
并更改Nit的run方法进行检查:
synchronized (this) {
while (!notified) {
wait();
}
}
然后在main方法中添加一行,以便主线程可以设置标志:
synchronized (n) {
n.notified = true;
n.notify();
}
这样通知仍然可以在n开始等待之前发生,但是在这种情况下,n将检查标志,看到它已经是真的,并且跳过等待。
请参阅Oracle的guarded blocks tutorial:
注意:始终在测试等待条件的循环内调用wait。
API文档(参见Thread.join)也不鼓励锁定线程对象。