简体   繁体   English

在同步块中的run()中使用this.wait()

[英]Using this.wait() inside run() in a synchronized block

I have this code: 我有以下代码:

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();
        }
    }
}

When I run it from cmd it never exits like it is an infinite loop. 当我从cmd运行它时,它永远不会像死循环那样退出。 I don't understand why. 我不明白为什么。 Only thing i can think of is that Nit n is still waiting but I don't get why? 我唯一能想到的是Nit n仍在等待,但我不明白为什么?

You are observing a race condition. 您正在观察种族状况。 You notify before the wait happens. 您在等待发生之前通知 Therefore the wait sits there and waits forever. 因此,等待坐在那里,永远等待。

If you would invoke this code often enough, you might see it passing sometimes - when the new thread advanced faster then the main thread. 如果您经常调用此代码,您可能会看到它有时通过-当新线程的运行速度快于主线程时。 One way to make the example work: try adding a call to Thread.sleep(1000) or so before calling notify() . 使示例工作的一种方法:尝试在调用notify()之前添加对Thread.sleep(1000)调用。 Alternatively, even a println() call on the main thread ( before the notify() might change timing enough). 或者,甚至在主线程上进行println()调用( notify()可能足够改变时序之前)。

Beyond that: such subtleties are the main reason why you actually avoid using the "low level" primitives such as as wait/notify. 除此之外:这些微妙之处是您实际上避免使用“低级”原语(例如,等待/通知)的主要原因。 Instead, you use the powerful abstractions (like queues) that standard APIs have to offer. 相反,您使用标准API必须提供的功能强大的抽象(例如队列)。

The notify method tells the scheduler to pick a thread to notify, choosing from only those threads that are currently waiting on the same lock that notify was called on. notify方法告诉调度程序选择一个要通知的线程,仅从那些当前正在等待调用了同一个锁的线程中进行选择。

In this case the n thread doesn't start waiting until after the notification has already happened, so nothing ever wakes the thread up from waiting. 在这种情况下,n线程直到通知已经发生后才开始等待,因此没有任何东西可以将线程从等待中唤醒。 You may have assumed that waiting threads will see notifications made before they started waiting, or that the JVM would have to give the n thread CPU time before the main thread proceeds past the call to start, but those assumptions aren't valid. 您可能已经假设等待线程在开始等待之前会看到通知,或者JVM必须在主线程继续通过调用开始之前给n线程CPU时间,但是这些假设是无效的。

Introduce a condition flag as an instance member of Nit: 将条件标志引入为Nit的实例成员:

public class Nit extends Thread {
    boolean notified = false;

and change Nit's run method to check it: 并更改Nit的run方法进行检查:

synchronized (this) {
    while (!notified) {
        wait();
    }
}

Then add a line to the main method so that the main thread can set the flag: 然后在main方法中添加一行,以便主线程可以设置标志:

synchronized (n) {
    n.notified = true;
    n.notify();
}

This way the notify can still happen before n starts waiting, but in that case n will check the flag, see it's true already, and skip waiting. 这样,通知仍然可以在n开始等待之前发生,但是在这种情况下,n将检查该标志,查看它是否为真,然后跳过等待。

See Oracle's guarded blocks tutorial : 请参阅Oracle的防护块教程

Note: Always invoke wait inside a loop that tests for the condition being waited for. 注意:始终在循环中调用wait,以测试正在等待的条件。

Also the API documentation (see Thread.join) discourages the practice of locking on thread objects. 此外,API文档(请参阅Thread.join)不建议锁定线程对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM