如何使用GenericObjectPool的borrowObject避免线程无限阻塞?
Apache Commons Pool2 2.8.1 对象池借取对象时的线程阻塞问题
配置对象池大小为1,启动3个线程借取对象时出现线程无限阻塞,应用直接挂起直至进程终止,具体执行流程如下:
- 第一个线程进入
borrowObject方法,因为没有空闲对象可通过pollFirst获取,所以创建新对象继续执行:
- 第一个线程进入
p = (PooledObject)this.idleObjects.pollFirst(); if (p == null) { p = this.create(); if (p != null) { create = true; } }
- 另外两个线程执行相同逻辑,检测到无空闲对象后尝试创建新对象,但由于池大小限制为1,无法创建新对象,转而进入
idleObjects.takeFirst()等待。此时blockWhenExhausted设为true,borrowMaxWaitMillis设为-1(无超时等待):
- 另外两个线程执行相同逻辑,检测到无空闲对象后尝试创建新对象,但由于池大小限制为1,无法创建新对象,转而进入
if (blockWhenExhausted) { if (p == null) { if (borrowMaxWaitMillis < 0L) { p = (PooledObject)this.idleObjects.takeFirst(); } else { p = (PooledObject)this.idleObjects.pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS); } } if (p == null) { throw new NoSuchElementException("Timeout waiting for idle object"); } }
- 第一个线程执行
this.factory.activateObject(p)激活对象后,调用this.factory.validateObject(p)验证对象,验证返回false(比如连接已断开),因此触发this.destroy(p)销毁该对象:
- 第一个线程执行
private void destroy(PooledObject<T> toDestroy) throws Exception { toDestroy.invalidate(); this.idleObjects.remove(toDestroy); this.allObjects.remove(new BaseGenericObjectPool.IdentityWrapper(toDestroy.getObject())); try { this.factory.destroyObject(toDestroy); } finally { this.destroyedCount.incrementAndGet(); this.createCount.decrementAndGet(); } }
- 对象被销毁后从
idleObjects和allObjects列表中移除,但另外两个线程仍在takeFirst()方法中等待从idleObjects取对象,导致无限期阻塞:
- 对象被销毁后从
public E takeFirst() throws InterruptedException { this.lock.lock(); Object var2; try { Object x; while((x = this.unlinkFirst()) == null) { this.notEmpty.await(); } var2 = x; } finally { this.lock.unlock(); } return var2; }
问题核心
destroy方法销毁对象并从idleObjects移除后,未唤醒等待在notEmpty条件队列上的线程。这些线程会一直卡在this.notEmpty.await()处,永远无法被唤醒,最终导致应用挂起。
内容的提问来源于stack exchange,提问作者Sart
相关产品推荐
相关产品推荐

