You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

notifyAll()无法唤醒全部线程问题:三线程场景下的异常排查

问题分析与解决方案

核心原因

你的代码里第二个线程无法被唤醒的关键问题在于:

  • methodB持有锁期间调用notifyAll,被唤醒的线程无法立即执行:methodB是synchronized方法,线程thread3Notify进入后会一直攥着main对象的锁,直到方法执行完才释放。第一次notifyAll会把thread0和thread1从等待队列里唤醒,但这俩线程得拿到锁才能继续执行wait()之后的代码,而锁还在thread3Notify手里,所以它们会陷入锁阻塞状态,而非等待状态。
  • 第二次notifyAll完全无效:当thread3Notify调用第二次notifyAll时,thread0和thread1已经不在等待队列里了,这次调用对它们没有任何作用。
  • 锁竞争的结果偏差:等methodB执行完释放锁,thread0和thread1会抢锁。如果thread0先拿到锁并跑完methodA,thread1拿到锁后本应继续执行,但你的输出里没出现,大概率是代码里sleep没正确处理异常(比如Thread.sleep()需要捕获InterruptedException),或者线程执行时机导致的表象,但本质问题还是锁持有时间太长,让唤醒逻辑失效了。

修正方案

要实现逐个唤醒线程的需求,得让每次唤醒后,被唤醒的线程有机会拿到锁执行,同时避免无意义的notifyAll调用。推荐两种可行方案:

方案1:用notify()逐个唤醒 + 释放锁等待

修改methodB,每次调用notify()后释放锁,等一段时间再重新拿锁唤醒下一个线程:

public void methodB() {
    synchronized (this) {
        System.out.println("唤醒第一个线程");
        this.notify();
    }
    try {
        Thread.sleep(1000); // 留时间给第一个线程执行
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    synchronized (this) {
        System.out.println("唤醒第二个线程");
        this.notify();
    }
}

同时给methodA加上循环等待(这是Java多线程的最佳实践,避免虚假唤醒):

public synchronized void methodA() {
    System.out.println("I enter methodA" + Thread.currentThread());
    try {
        this.wait();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    System.out.println("I am awake " + Thread.currentThread());
}

方案2:用notifyAll()配合条件变量

如果必须用notifyAll(),可以通过条件变量标记需要唤醒的线程,让线程在wait()前检查条件,确保只有满足条件的线程才继续执行:

private int wakeCount = 0;

public synchronized void methodA() {
    System.out.println("I enter methodA" + Thread.currentThread());
    try {
        // 循环等待,避免虚假唤醒,直到满足计数条件
        while (wakeCount == 0) {
            this.wait();
        }
        wakeCount--;
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    System.out.println("I am awake " + Thread.currentThread());
}

public synchronized void methodB() {
    try {
        wakeCount++;
        this.notifyAll();
        Thread.sleep(1000);
        wakeCount++;
        this.notifyAll();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}

关键注意事项

  • 永远在while循环里调用wait(),防止虚假唤醒(线程可能没被notify/notifyAll就醒来)。
  • 调用wait()、notify()、notifyAll()时,必须持有对应对象的锁。
  • Thread.sleep()不会释放锁,只有wait()会主动释放锁。

内容的提问来源于stack exchange,提问作者Klaus Köhler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 00:09:24