pollLast( )
核心逻辑是自旋,判断是否有可用连接,然后发送empty消息,通知生产者线程可以创建连接。之后阻塞wait。只不过这个方法需要设置超时时间。
private DruidConnectionHolder pollLast(long nanos) throws InterruptedException, SQLException {
// estimate超时时间
long estimate = nanos;
for (;;) {
// 判断连接池可用连接poolingCount是否为0
if (poolingCount == 0) {
// 池子中已经没有可用的连接了,唤起添加连接的线程
emptySignal(); // send signal to CreateThread create connection
if (failFast && isFailContinuous()) {
throw new DataSourceNotAvailableException(createError);
}
if (estimate <= 0) {
waitNanosLocal.set(nanos - estimate);
return null;
}
notEmptyWaitThreadCount++;
if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) {
notEmptyWaitThreadPeak = notEmptyWaitThreadCount;
}
try {
long startEstimate = estimate;
// 陷入等待状态,开始参与获取连接的竞争
// 每次唤醒时estimate会被刷新,estimate=最大阻塞时间-实际阻塞时间
// 下次被刷新后就是maxWait减去上次阻塞花费的实际时间
// 每次await的时间会逐步减少,直到归零,整体时间是约等于maxWait
estimate = notEmpty.awaitNanos(estimate); // signal by
// recycle or
// creator
notEmptyWaitCount++;
/**
* await被唤起后继续加入锁竞争,然后往下走如果发现池子里的连接数仍然是0(说明在唤醒后参与锁竞争里刚被放进来的连接又被别的线程拿去了),
* 则继续下一次的await,这里采用的是awaitNanos方法,初始值是maxWait,然后下次被刷新后就是maxWait减去上次阻塞花费的实际时间,
* 每次await的时间会逐步减少,直到归零,整体时间是约等于maxWait的,但实际比maxActive要大,
* 因为程序本身存在耗时以及被唤醒后又要参与锁竞争导致也存在一定的耗时。
*/
notEmptyWaitNanos += (startEstimate - estimate);
if (!enable) {
connectErrorCountUpdater.incrementAndGet(this);
if (disableException != null) {
throw disableException;
}
throw new DataSourceDisableException();
}
} catch (InterruptedException ie) {
notEmpty.signal(); // propagate to non-interrupted thread
notEmptySignalCount++;
throw ie;
} finally {
notEmptyWaitThreadCount--;
}
if (poolingCount == 0) {
// 判断是否需要进行新一轮的获取连接操作
if (estimate > 0) {
continue;
}
waitNanosLocal.set(nanos - estimate);
return null;
}
}
// poolingCount--
decrementPoolingCount();
// 取到最后一个连接
DruidConnectionHolder last = connections[poolingCount];
// 设置连接池中此连接为空(之后回归还吗?)
connections[poolingCount] = null;
long waitNanos = nanos - estimate;
last.setLastNotEmptyWaitNanos(waitNanos);
return last;
}
}