【问题标题】:Strange java behavior of wait/notify等待/通知的奇怪java行为
【发布时间】:2026-01-09 12:20:05
【问题描述】:

我发现了 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() 被注释掉了?如果它处于活动状态,那么我也希望代码不会挂断。

标签: java multithreading concurrency


【解决方案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();

【讨论】:

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