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

如何唤醒不同实例的线程?线程同步后无法唤醒问题求助

问题出在锁对象和wait/notify的绑定错误上

兄弟,你的代码里犯了一个典型的线程同步错误——混用了两个完全无关的锁对象,直接导致wait/notify机制彻底失效了,咱们一步步拆解:

1. 你违反了wait/notify的核心规则

不管是wait()还是notifyAll(),都有一个硬性要求:当前线程必须持有该对象的内置锁(也就是进入synchronized(obj)块,或者在synchronized方法里)才能调用。

看你的代码:

  • 你用obj1.getLock().tryLock()获取的是obj1.getLock()这个对象的锁,但你调用的是obj1.wait()——这时候你根本没持有obj1的锁,所以这里会偷偷抛出IllegalMonitorStateException!你的catch块只打印了个"Error!",大概率这个异常已经发生了,只是你没看到详细信息,线程的执行流程直接被打断了。
  • 同理,你调用obj1.notifyAll()的时候,也没持有obj1的锁,同样会抛异常,导致notifyAll()根本没起到唤醒作用,那些卡在obj1.wait()的线程自然永远醒不过来。

2. wait/notify必须和同一个锁绑定

wait/notify机制是和锁对象绑定的,你要么:

  • 用obj1作为锁对象,统一在synchronized(obj1)块里调用obj1.wait()和obj1.notifyAll();
  • 要么用obj1.getLock()这个锁(如果是ReentrantLock这类显式锁),搭配它的Condition对象来做等待唤醒。

给你修正后的两种方案

方案一:用内置锁(synchronized)+ wait/notifyAll

这种方式更简单,适合基础场景:

public boolean addToList() {
    synchronized (obj1) {
        try {
            // 用while循环重新检查条件,防止虚假唤醒
            while (obj1.getPerson().size() >= obj1.getNum()) {
                System.out.println(this.name + " 列表已满,等待中...");
                obj1.wait(); // 这里会释放obj1的锁,进入等待
            }

            // 执行添加逻辑
            System.out.println("0 " + name + " list size: " + obj1.getPerson().size());
            obj1.getPerson().add(this);
            System.out.println("1 " + name + " list size: " + obj1.getPerson().size());
            
            // 唤醒所有等待的线程
            obj1.notifyAll();
            System.out.println(this.name + " 完成添加,已唤醒其他线程");
            return true;
        } catch (InterruptedException e) {
            System.out.println(this.name + " 被中断:" + e.getMessage());
            Thread.currentThread().interrupt(); // 重置中断状态
            return false;
        }
    }
}

方案二:用显式Lock + Condition(如果obj1.getLock()是ReentrantLock)

如果你的obj1.getLock()是java.util.concurrent.locks.ReentrantLock,可以用更灵活的Condition机制:

public boolean addToList() {
    Lock lock = obj1.getLock();
    Condition condition = lock.newCondition(); // 绑定当前锁的Condition
    try {
        lock.lock(); // 确保获取锁
        System.out.println(this.name + " 获取锁成功");

        // 循环检查条件,防止虚假唤醒
        while (obj1.getPerson().size() >= obj1.getNum()) {
            System.out.println(this.name + " 列表已满,等待中...");
            condition.await(); // 释放锁,进入等待
        }

        // 执行添加逻辑
        System.out.println("0 " + name + " list size: " + obj1.getPerson().size());
        obj1.getPerson().add(this);
        System.out.println("1 " + name + " list size: " + obj1.getPerson().size());
        
        // 唤醒所有等待的线程
        condition.signalAll();
        System.out.println(this.name + " 完成添加,已唤醒其他线程");
        return true;
    } catch (InterruptedException e) {
        System.out.println(this.name + " 被中断:" + e.getMessage());
        Thread.currentThread().interrupt();
        return false;
    } finally {
        lock.unlock(); // 必须在finally里解锁,防止异常导致锁泄漏
        System.out.println(this.name + " 释放锁");
    }
}

额外要注意的点

  • 永远用while循环检查等待条件,而不是if——因为线程被唤醒后,条件可能已经被其他线程修改了,必须重新验证。
  • 显式锁的解锁一定要放在finally块里,不然如果添加逻辑抛出异常,锁会被永久持有,其他线程永远无法获取。

内容的提问来源于stack exchange,提问作者Bob Rob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:34:35