在synchronized块中的run()内使用this.wait()

时间:2017-08-14 13:47:09

标签: java multithreading

我有这段代码:

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还在等待,但我不明白为什么?

2 个答案:

答案 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)也不鼓励锁定线程对象。