简体   繁体   English

意外的线程唤醒

[英]Unexpected thread wakeup

I was expecting the second thread in the following example to hang, since it waits on an object with no corresponding notify. 我期待以下示例中的第二个线程挂起,因为它等待没有相应通知的对象。 Instead, it falls through to the println, presumably due to a spurious wakeup. 相反,它落到了println,可能是由于虚假的唤醒。

public class Spurious {
    public static void main(String[] args) {

        Thread t1 = new Thread() { 
            public void run() { 
                System.out.println("Hey!"); 
            }  
        };
        Thread t2 = new Thread() { 
            public void run() 
            {
                try {
                    synchronized (t1) {
                        t1.wait();
                    }
                } catch (InterruptedException e) {
                    return;
                }
                System.out.println("Done.");
            }
        };
        t1.start();
        t2.start();
    }
}

Output: 输出:

Hey!
Done.

On the other hand, if one removes the "Hey!" 另一方面,如果删除“嘿!” println from the first thread, the second thread will indeed hang. println从第一个线程开始,第二个线程确实会挂起。 This happens on both MacOS and Linux. 这种情况发生在MacOS和Linux上。

Any idea why? 知道为什么吗?

This is not a spurious wakeup, a spurious wakeup is caused by a race condition in the JVM. 这不是一个虚假的唤醒,一个虚假的唤醒是由JVM中的竞争条件引起的。 This is a race condition in your code. 这是您的代码中的竞争条件。

The println keeps thread1 busy just long enough that thread2 can start waiting before thread1 terminates. println使thread1保持忙碌的时间足够长,以至于thread2可以在thread1终止之前开始等待。 Once thread1 terminates it sends a notification to everything waiting on its monitor. 一旦thread1终止,它就会向其监视器上等待的所有内容发送通知。 thread2 receives the notification and ceases waiting. thread2收到通知并停止等待。

Removing the println reduces the time needed for thread1 to finish dramatically so that thread1 has already finished by the time thread2 can start waiting on it. 删除println会减少thread1显着完成所需的时间,以便在thread2开始等待它时,thread1已经完成。 thread1 is no longer alive and its notification has already occurred before thread2 started waiting, so thread2 waits forever. thread1不再处于活动状态,并且在thread2开始等待之前已经发生了通知,因此thread2将永远等待。

That threads send a notification when they die is documented in the API for Thread#join : Thread for join的API中记录了线程在死亡时发送通知:

This implementation uses a loop of this.wait calls conditioned on this.isAlive. 此实现使用this.wait调用this.isAlive的循环。 As a thread terminates the this.notifyAll method is invoked. 当一个线程终止时,将调用this.notifyAll方法。 It is recommended that applications not use wait, notify, or notifyAll on Thread instances. 建议应用程序不要在Thread实例上使用wait,notify或notifyAll。

The moral (well, one of the morals) is to always wait in a loop with a condition variable, see the Oracle tutorial . 道德(嗯,道德之一)总是在一个带有条件变量的循环中等待,参见Oracle教程 If you change Thread2 to look like this: 如果您将Thread2更改为如下所示:

    Thread t2 = new Thread() { 
        public void run() 
        {
            try {
                synchronized (t1) {
                    while (t1.isAlive()) {
                        t1.wait();
                    }
                }
            } catch (InterruptedException e) {
                return;
            }
            System.out.println("Done.");
        }
    };

then thread2 should exit regardless of whether thread2 can start waiting before thread1 finishes. 然后thread2应该退出,无论thread2是否可以在thread1完成之前开始等待。

Of course this is total toy example territory: 当然这是一个完整的玩具示例区域:

  • Don't extend Thread. 不要扩展Thread。

  • Don't lock on threads. 不要锁定线程。

  • Don't start Threads, use Executors. 不要启动线程,使用Executors。

  • Prefer higher level concurrency constructs to wait/notify. 更喜欢更高级别的并发结构来等待/通知。

You're waiting on a Thread object. 你正在等待一个Thread对象。 That's bad practice, that is explicitly discouraged in the javadoc of Thread (Thread.join, precisely). 这是不好的做法,在Thread的线程(Thread.join)中明确地不鼓励这种做法。

The reason is that when you call thread.join() to block until the thread stops running, you're actually waiting on the thread. 原因是当你调用thread.join()来阻塞直到线程停止运行时,你实际上在等待线程。 And when the thread stops running, it notifies in order to unblock all the callers of join() . 当线程停止运行时,它会通知以取消阻止join()所有调用者。

Since you waited on the thread, you're implicitly being notified when the thread stops running. 由于您在线程上等待,因此在线程停止运行时会隐式通知您。

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

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