等待/通知的奇怪java行为

Posted

技术标签:

【中文标题】等待/通知的奇怪java行为【英文标题】:Strange java behavior of wait/notify 【发布时间】:2014-04-10 21:06:55 【问题描述】:

我发现了 java 并发的奇怪行为。请参阅下面的下一个代码:

公共类测试 静态 CountDownLatch 锁存器 = 新的 CountDownLatch(1); public static void main(String[] args) 抛出 UnsupportedEncodingException, InterruptedException 最终线程 t = new MyThread(); t.start(); 同步(t) 锁存器.countDown(); System.out.println("要睡觉了"); t.wait(); System.out.println("唤醒"); 静态类 MyThread 扩展 Thread @覆盖 公共无效运行() 尝试 锁存器.await(); 捕捉(InterruptedException e) e.printStackTrace(); 同步(这个) System.out.println("内部运行"); // notifyAll();

在我看来,这段代码应该挂断并永远等待,但是代码已经完成,控制台中的 next out 没有任何问题:

睡觉了 内跑 醒来

我试图找到一些关于在线程死亡时通知锁的信息,但缺少它。 我也没有在 java 规范中找到任何信息。

但是,如果我尝试锁定其他对象(而不是线程对象),它可以正常工作。

【问题讨论】:

你锁定线程实例上的同步,运行线程,然后在 Main 中等待线程。在这一点上,线程应该有统治权。它在 this 上同步,这与 Main 等待的相同,然后调用 notifyAll(),它释放 Main 中的等待。该应用程序应该可以正常运行。 另外,not 在线程上调用 wait/notify/notifyAll - 它在内部使用它。哦,更喜欢实现 Runnable 而不是扩展 Thread :) 为什么 notifyAll() 被注释掉了?如果它处于活动状态,那么我也希望代码不会挂断。 【参考方案1】:

这是因为您正在等待 Thread 实例。 Thread 在内部使用wait/notify/notifyAll,所以你也不应该自己这样做——你会混淆Thread,而Thread 会混淆你。特别是当一个线程退出时,它会调用this.notifyAll()

来自Thread.join的文档:

此实现使用以this.isAlive 为条件的this.wait 调用循环。当一个线程终止时,this.notifyAll 方法被调用。建议应用程序不要在Thread 实例上使用waitnotifynotifyAll

一般来说,尝试锁定并等待 nothing else 可以与之交互的对象。这样您就可以推断对象上存在的与并发相关的操作,因为它们非常有限。只要任意代码可以在对象上同步(并调用等待/通知等),就很难证明您的代码是正确的。这就是为什么您会经常看到类似的内容:

public class Foo 
    private final Object lock = new Object();

    ... code which uses synchronized(lock) ...

【讨论】:

感谢您的回答。我知道这是不好的做法。这就像java拼图。我的朋友问过这个问题,我无法解释为什么它不起作用。 @EvgeniyKiselev:嗯,希望现在你可以:) 将来,我会在问题中添加一些上下文指示。在这种情况下问题不大,但通常会有所帮助。 即使它不是线程实例也可能发生这种情况,因为允许虚假唤醒。【参考方案2】:

正如 Jon Skeet 所写,在 Thread 对象上同步是个坏主意,请使用另一个对象:

public class Test 
    static CountDownLatch latch = new CountDownLatch(1);
    static final Object sync = new Object();

    public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException 
        final Thread t = new MyThread();
        t.start();
        synchronized (sync) 
            latch.countDown();
            System.out.println("got to sleep");
            sync.wait();
            System.out.println("wake up");
        
    


static class MyThread extends Thread 
    @Override
    public void run() 
        try 
            latch.await();
         catch (InterruptedException e) 
            e.printStackTrace();
        
        synchronized (sync) 
            System.out.println("inside run");
            //sync.notifyAll();
        
    

现在这段代码永远不会结束,直到你取消注释 sync.notifyAll();

【讨论】:

我知道这是不好的做法,而且我知道如果锁定不同的对象,它会正常工作。

以上是关于等待/通知的奇怪java行为的主要内容,如果未能解决你的问题,请参考以下文章

Java并发编程之十二:线程间通信中notifyAll造成的早期通知问题(含代码)

转:Java并发编程之十二:线程间通信中notifyAll造成的早期通知问题(含代码)

Java 中啥是快速、等待通知或忙等待?

Flutter:共享首选项的奇怪行为

奇怪的NSURLSessionDownloadTask行为通过蜂窝(不是wifi)

二 Java利用等待/通知机制实现一个线程池