Druid的守护线程CreateConnectionThread
当不存在创建任务createScheduler
时,则创建守护线程CreateConnectionThread
。该线程的主要作用是创建连接。
protected void createAndStartCreatorThread() {
if (createScheduler == null) {
String threadName = "Druid-ConnectionPool-Create-" + System.identityHashCode(this);
createConnectionThread = new CreateConnectionThread(threadName);
createConnectionThread.start();
return;
}
initedLatch.countDown();
}
涉及知识点
CountDownLatch计数器
方法 | 说明 |
---|---|
await() | 使当前线程进入同步队列进行等待,直到latch的值被减到0或者当前线程被中断,当前线程就会被唤醒。 |
await(long timeout, TimeUnit unit) | 带超时时间的await() 。 |
countDown() | 使latch 的值减1,如果减到了0,则会唤醒所有等待在这个latch 上的线程。 |
getCount() | 获得latch 的数值。 |
Condition
Condition是在java 1.5中才出现的,它⽤来替代传统的Object的wait()、notify()实现线程间的协作,相⽐使⽤Object的wait()、notify(),使⽤Condition的await()、signal()这种⽅式实现线程间协作更加安全和⾼效。
CreateConnectionThread源码分析
public void run() {
//计数器减一,等待唤醒。
initedLatch.countDown();
long lastDiscardCount = 0;
int errorCount = 0;
for (;;) {
//获取锁,发生中断异常,则结束该线程。
try {
lock.lockInterruptibly();
} catch (InterruptedException e2) {
break;
}
long discardCount = DruidDataSource.this.discardCount;
boolean discardChanged = discardCount - lastDiscardCount > 0;
lastDiscardCount = discardCount;
try {
boolean emptyWait = true;
if (createError != null
&& poolingCount == 0
&& !discardChanged) {
emptyWait = false;
}
if (emptyWait
&& asyncInit && createCount < initialSize) {
emptyWait = false;
}
if (emptyWait) {
// 必须存在线程等待,才创建连接
if (poolingCount >= notEmptyWaitThreadCount //
&& (!(keepAlive && activeCount + poolingCount < minIdle))
&& !isFailContinuous()
) {
//释放锁,进入等待状态。直到唤醒
empty.await();
}
// 防止创建超过maxActive数量的连接
if (activeCount + poolingCount >= maxActive) {
empty.await();
continue;
}
}
} catch (InterruptedException e) {
lastCreateError = e;
lastErrorTimeMillis = System.currentTimeMillis();
if ((!closing) && (!closed)) {
LOG.error("create connection Thread Interrupted, url: " + jdbcUrl, e);
}
break;
} finally {
lock.unlock();
}
PhysicalConnectionInfo connection = null;
try {
//创建连接
connection = createPhysicalConnection();
} catch (SQLException e) {
LOG.error("create connection SQLException, url: " + jdbcUrl + ", errorCode " + e.getErrorCode()
+ ", state " + e.getSQLState(), e);
errorCount++;
if (errorCount > connectionErrorRetryAttempts && timeBetweenConnectErrorMillis > 0) {
// fail over retry attempts
setFailContinuous(true);
if (failFast) {
lock.lock();
try {
notEmpty.signalAll();
} finally {
lock.unlock();
}
}
if (breakAfterAcquireFailure) {
break;
}
try {
Thread.sleep(timeBetweenConnectErrorMillis);
} catch (InterruptedException interruptEx) {
break;
}
}
} catch (RuntimeException e) {
LOG.error("create connection RuntimeException", e);
setFailContinuous(true);
continue;
} catch (Error e) {
LOG.error("create connection Error", e);
setFailContinuous(true);
break;
}
//连接创建失败,则重新进入循环
if (connection == null) {
continue;
}
//将创建的连接放入池中
boolean result = put(connection);
//放入失败,则关闭该连接
if (!result) {
JdbcUtils.close(connection.getPhysicalConnection());
LOG.info("put physical connection to pool failed.");
}
errorCount = 0; // reset errorCount
if (closing || closed) {
break;
}
}
}
调试查看
当用户设置initial-size
时,因为initial-size
不等0。
会在init()
方法中直接进行初始化连接,后续创建连接是通过CreateConnectionThread
。
while (poolingCount < initialSize) {
try {
//创建连接
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
//将连接放入池中
connections[poolingCount++] = holder;
} catch (SQLException ex) {
LOG.error("init datasource error, url: " + this.getUrl(), ex);
if (initExceptionThrow) {
connectError = ex;
break;
} else {
Thread.sleep(3000);
}
}
}