如何唤醒不同实例的线程?线程同步后无法唤醒问题求助
问题出在锁对象和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
相关产品推荐
相关产品推荐

