对象池(Object Pool)notify/notifyAll方法失效问题求助
嘿,我之前在实现对象池的时候也踩过这个坑,大概率是这几个常见问题导致的,咱们逐个排查:
1. 等待与唤醒的锁对象不匹配
wait()、notify()、notifyAll()这三个方法的调用必须基于同一个锁对象的同步块/方法里。如果你的借对象逻辑用了锁A调用wait(),归还时却用锁B调用notify(),那等待的线程根本接收不到唤醒信号。
举个错误的例子:
// 借对象时用this当锁 synchronized(this) { while(pool.isEmpty()) { this.wait(); } } // 归还对象时用队列当锁 synchronized(poolQueue) { poolQueue.add(obj); poolQueue.notifyAll(); }
修正方案:统一锁对象,比如直接用对象池的队列作为锁:
// 借对象逻辑 synchronized(poolQueue) { while(poolQueue.isEmpty()) { poolQueue.wait(); // 基于poolQueue锁等待 } return poolQueue.poll(); } // 归还对象逻辑 synchronized(poolQueue) { poolQueue.add(returnedObj); poolQueue.notifyAll(); // 基于同一个poolQueue锁唤醒 }
2. 误用notify()而非notifyAll()
notify()只会随机唤醒一个等待该锁的线程,如果你的对象池有多个等待线程,或者被唤醒的线程因为队列状态变化(比如刚被唤醒就被其他线程抢了对象)再次进入等待,那剩下的线程就会一直处于等待状态,看起来像没被唤醒。
修正方案:优先使用notifyAll(),它会唤醒所有等待该锁的线程,让它们重新检查队列状态,确保至少有一个线程能拿到对象。
3. wait()没有放在循环中
线程可能会遇到虚假唤醒(Spurious Wakeup)——也就是没有被notify()/notifyAll()调用就自行唤醒。如果你的等待逻辑用的是if判断,线程被虚假唤醒后会直接执行后续代码,可能拿到空对象;同时后续的notify()也无法正确触发线程重新检查状态。
错误示例:
synchronized(lockObj) { if(poolQueue.isEmpty()) { // 用if会导致虚假唤醒问题 lockObj.wait(); } return poolQueue.poll(); }
修正方案:把wait()放在while循环里,每次唤醒后重新检查队列是否为空:
synchronized(lockObj) { while(poolQueue.isEmpty()) { // 循环检查,避免虚假唤醒 lockObj.wait(); } return poolQueue.poll(); }
4. 并发容器的同步逻辑混乱
你用了ConcurrentLinkedQueue,它本身是线程安全的,但wait()/notify()依赖的是锁的条件队列,如果你在同步块外操作队列,再在同步块内调用notify(),会导致队列状态和锁的同步状态不一致,线程无法感知到队列的变化。
修正方案:所有涉及队列状态检查、修改的操作,以及wait()/notify()调用,都要放在同一个锁的同步块内,保证状态的可见性。
先检查这几个点,大概率能解决你的唤醒问题!
内容的提问来源于stack exchange,提问作者springcloudlearner

