线程意外唤醒

2024-03-09

我预计以下示例中的第二个线程会挂起,因为它等待一个没有相应通知的对象。相反,它会进入 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.

另一方面,如果删除“嘿!” println 从第一个线程,第二个线程确实会挂起。 MacOS 和 Linux 上都会发生这种情况。

知道为什么吗?


这不是虚假唤醒,虚假唤醒是由 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 都应该退出。

当然,这是完整的玩具示例领域:

  • 不要扩展 Thread,使用 Runnable 或 Callable。

  • 不要锁定线程。

  • 不要启动线程,使用执行器。

  • 更喜欢使用更高级别的并发结构来等待/通知。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程意外唤醒 的相关文章

随机推荐