所属文章:池化技术(一)Druid是如何管理数据库连接的?

本代码段对应主流程5,连接的回收:


//DruidPooledConnection类的close方法
@Override
public void close() throws SQLException {
if (this.disable) { //检查,因为该连接对象是抛出去给别的业务线程使用,也就是说并不受连接池本身管控,所以很可能存在多线程同时close的操作,因此这里需要做一层检查,包括下方的syncClose里的检查也是一个意思
return;
} DruidConnectionHolder holder = this.holder; //拿到对应的holder对象(之前说过,这个对象才是最后放进连接池的对象)
if (holder == null) {
if (dupCloseLogEnable) {
LOG.error("dup close");
}
return;
} DruidAbstractDataSource dataSource = holder.getDataSource(); //拿到对应的连接池对象
boolean isSameThread = this.getOwnerThread() == Thread.currentThread(); if (!isSameThread) { //关闭该连接与获取该连接的线程并非同一个的时候,则触发下面的syncClose
dataSource.setAsyncCloseConnectionEnable(true);
} if (dataSource.isAsyncCloseConnectionEnable()) {
syncClose(); //参考上面的解释,该方法详情在下方
return;
} //一些事件监听器的触发,忽略
for (ConnectionEventListener listener : holder.getConnectionEventListeners()) {
listener.connectionClosed(new ConnectionEvent(this));
} //责任链的执行,参考流程1.1与代码段1-2,运行方式是一样的,找到映射方法,整个触发一遍责任链上的filters
List filters = dataSource.getProxyFilters();
if (filters.size() > 0) {
FilterChainImpl filterChain = new FilterChainImpl(dataSource);
filterChain.dataSource_recycle(this);
} else {
recycle(); //触发目标方法recycle
} this.disable = true; //标记该连接已失效,无法再次提供服务
} //上面逻辑走syncClose的情况,该方法与上面大体相同,但由于不是同一个线程做的操作,所以这里需要锁控制
public void syncClose() throws SQLException {
lock.lock(); //获取锁,这个锁是当前连接对象上的锁,为了解决同一个连接对象在不同的线程里被同时close多次而造成的线程安全问题
try {
if (this.disable) {
return;
} DruidConnectionHolder holder = this.holder; //同样的,拿到需要归还的holder对象
if (holder == null) {
if (dupCloseLogEnable) {
LOG.error("dup close");
}
return;
} //同样是一些事件监听器的触发,忽略
for (ConnectionEventListener listener : holder.getConnectionEventListeners()) {
listener.connectionClosed(new ConnectionEvent(this));
} //同样的责任链的执行,参考上面的解释
DruidAbstractDataSource dataSource = holder.getDataSource();
List filters = dataSource.getProxyFilters();
if (filters.size() > 0) {
FilterChainImpl filterChain = new FilterChainImpl(dataSource);
filterChain.dataSource_recycle(this);
} else {
recycle(); //触发目标方法recycle,方法详情在下方
} this.disable = true; //标记该连接已失效,无法再次提供服务
} finally {
lock.unlock(); //解锁
}
} //DruidPooledConnection类的recycle方法,由上面俩方法直接触发
public void recycle() throws SQLException {
if (this.disable) {
return;
} DruidConnectionHolder holder = this.holder; //拿到真正需要归还的连接对象
if (holder == null) {
if (dupCloseLogEnable) {
LOG.error("dup close");
}
return;
} if (!this.abandoned) { //如果期间已经被流程4.2处理过了(abandoned==true),则不触发下方逻辑
DruidAbstractDataSource dataSource = holder.getDataSource();
dataSource.recycle(this); //真正触发连接池的回收方法,方法详情在下方
} //连接对象一旦被回收处理,则会把所有与连接相关的属性置空(不持有),closed标记为true
this.holder = null;
conn = null;
transactionInfo = null;
closed = true;
} //DruidDataSource类里的recycle方法,真正回收连接的方法,由上面DruidPooledConnection类的recycle触发
protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
final DruidConnectionHolder holder = pooledConnection.holder; if (holder == null) {
LOG.warn("connectionHolder is null");
return;
} if (logDifferentThread //
&& (!isAsyncCloseConnectionEnable()) //
&& pooledConnection.ownerThread != Thread.currentThread()//
) {
LOG.warn("get/close not same thread");
} final Connection physicalConnection = holder.conn; //拿到真正的驱动连接对象 if (pooledConnection.traceEnable) { //如果traceEnable为true(满足该属性为true,必须要removeAbandoned设置为true,这样在主流程1那里才会被放进activeConnections,才会置为true),流程4.2处理过后,会把该属性重新置为false,其他情况均为true
Object oldInfo = null;
activeConnectionLock.lock();
try {
if (pooledConnection.traceEnable) { //双重检查
oldInfo = activeConnections.remove(pooledConnection); //从activeConnections移除,防止流程4.2的重复检查
pooledConnection.traceEnable = false; //置为false
}
} finally {
activeConnectionLock.unlock();
}
if (oldInfo == null) {
if (LOG.isWarnEnabled()) {
LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size());
}
}
} final boolean isAutoCommit = holder.underlyingAutoCommit;
final boolean isReadOnly = holder.underlyingReadOnly;
final boolean testOnReturn = this.testOnReturn; try {
// 如果在归还至连接池时发现此连接对象还有未处理完的事务,则直接回滚
if ((!isAutoCommit) && (!isReadOnly)) {
pooledConnection.rollback();
} // reset holder, restore default settings, clear warnings
boolean isSameThread = pooledConnection.ownerThread == Thread.currentThread();
if (!isSameThread) { //同样判断线程,为了保证安全性
final ReentrantLock lock = pooledConnection.lock;
lock.lock();
try {
holder.reset(); //连接被借出去后,可能被业务方改动了一些属性(典型的比如autoCommit),现在利用reset方法还原为默认值
} finally {
lock.unlock();
}
} else {
holder.reset(); //同上,这里认为获取和关闭连接的是同一个线程,不存在线程安全问题,因此不用去竞争锁
} //连接已被抛弃,则不作任何处理(不再归还)
if (holder.discard) {
return;
} //忽略
if (phyMaxUseCount > 0 && holder.useCount >= phyMaxUseCount) {
discardConnection(holder.conn);
return;
} //如果驱动连接本身被人为关闭了,除一些监控值之外,也不做处理
if (physicalConnection.isClosed()) {
lock.lock();
try {
activeCount--;
closeCount++;
} finally {
lock.unlock();
}
return;
} //参考testOnBorrow,这里testOnReturn就是指每次回收连接都要做连接可用性检查,同样官方不建议开启,影响性能,缺省值也是不开启的
if (testOnReturn) {
//流程忽略
boolean validate = testConnectionInternal(holder, physicalConnection);
if (!validate) {
JdbcUtils.close(physicalConnection); destroyCountUpdater.incrementAndGet(this); lock.lock();
try {
activeCount--;
closeCount++;
} finally {
lock.unlock();
}
return;
}
} if (!enable) {
//中途发现连接又被置为不可用,则直接触发抛弃方法,参考流程1.4和代码段1-5
discardConnection(holder.conn);
return;
} boolean result;
final long currentTimeMillis = System.currentTimeMillis(); if (phyTimeoutMillis > 0) {
long phyConnectTimeMillis = currentTimeMillis - holder.connectTimeMillis;
if (phyConnectTimeMillis > phyTimeoutMillis) {
discardConnection(holder.conn);
return;
}
} lock.lock();
try {
activeCount--;
closeCount++; //最终放入池子,方法详情在下方
result = putLast(holder, currentTimeMillis);
recycleCount++;
} finally {
lock.unlock();
} if (!result) { //如果加不进去,则直接关闭驱动连接,然后不处理(此时holder已经失去强引用,不久便会被回收)
JdbcUtils.close(holder.conn);
LOG.info("connection recyle failed.");
}
} catch (Throwable e) {
holder.clearStatementCache(); if (!holder.discard) {
this.discardConnection(physicalConnection);
holder.discard = true;
} LOG.error("recyle error", e);
recycleErrorCountUpdater.incrementAndGet(this);
}
} //DruidDataSource类里的putLast方法,由上方的recycle方法触发
boolean putLast(DruidConnectionHolder e, long lastActiveTimeMillis) {
if (poolingCount >= maxActive) { //池子已满,不加
return false;
} e.lastActiveTimeMillis = lastActiveTimeMillis; //刷新上次活跃时间,该时间很重要,直接影响连接检查的触发
connections[poolingCount] = e; //放进连接池数组尾部
incrementPoolingCount(); //poolingCount++ if (poolingCount > poolingPeak) {
poolingPeak = poolingCount;
poolingPeakTime = lastActiveTimeMillis;
} notEmpty.signal(); //因为成功回收了一个连接,那就唤起一次所有因为获取不到连接而被阻塞的业务线程吧~(参考流程1.2)
notEmptySignalCount++; return true;
}

Druid-代码段-5-1的更多相关文章

  1. WPF自定义RoutedEvent事件代码段

    今天在写东西的时候,发现常用的代码段里没有RoutedEvent的,因此,写了一个代码段,方便以后使用,顺便记录一下,如何做代码段. 1.在项目中新建一个XML文件,将扩展名修改为snippet. 2 ...

  2. JavaScript代码段整理笔记系列(二)

    上篇介绍了15个常用代码段,本篇将把剩余的15个补齐,希望对大家有所帮助!!! 16.检测Shift.Alt.Ctrl键: event.shiftKey; //检测Shift event.altKey ...

  3. 我们为什么要看《超实用的Node.JS代码段》

    不知道自己Node.JS水平如何?看这张图 如果一半以上的你都不会,必须看这本书,一线工程师用代码和功能页面来告诉你每一个技巧点. 都会一点,但不知道如何检验自己,看看本书提供的面试题: 1.     ...

  4. 《超实用的JavaScript代码段》—— 读后总结

    这本书全是代码,从头到尾跟着坐下来确实收获很多.比那些古板的教科书式的理解更多,不过书中并不是每个例子都做了,有的作者封装的太多,觉得看了收获不多,就没细看——比如模块渐变.有空好好学学这段的代码. ...

  5. Visual Studio常用小技巧一:代码段+快捷键+插件=效率

    用了visual studio 5年多,也该给自己做下备忘录了.每次进新的组换新的电脑,安装自己熟悉的环境又得重新配置,不做些备忘老会忘记一些东西.工具用的好,效率自然翻倍. 1,代码段 在Visua ...

  6. 使用eclipse开发Morphline的Java代码段

    背景:morphline是一个轻量级的etl工具.除了提供标准化的方法之外,还可以定制化的开发java片段.定制化的java片段会在加载时被作为一个独立的类编译,对源数据作处理. morphline关 ...

  7. 前端福利!10个短小却超实用的JavaScript 代码段

    JavaScript正变得越来越流行,它已经成为前端开发的第一选择,并且利用基于JavaScript语言的NodeJS,我们也可以开发出高 性能的后端服务,甚至我还看到在硬件编程领域也出现了JavaS ...

  8. Visual C# 代码段

    代码段是现成的代码段,您可以快速将其插入到您的代码中. 例如,for 代码段创建一个空的 for 循环. 有些代码段为外侧代码段,这些代码段允许您先选择代码行,然后选择要并入选定代码行的代码段. 例如 ...

  9. 十五个常用的jquery代码段【转】

    好的文章顶一个 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top 2 $('a.t ...

  10. 十五个常用的jquery代码段

    十五个常用的jquery代码段 回到顶部按钮 通过使用 jQuery 中的 animate 和 scrollTop 方法,你无需插件便可创建一个简单地回到顶部动画: 1 // Back to top ...

随机推荐

  1. 带你自行搭建虚拟机和Redis集群环境,值得收藏!

    前言: 我们看到分析 Redis 使用或原理的文章不少,但是完整搭建一套独立的 redis 集群环境的介绍,并不是很多或者说还不够详细. 那么,本文会手把手带着大家搭建一套 Redis 集群环境,Re ...

  2. 从零开始ant-design-vue-pro开发笔记(一)

    开始 从这里开始是用ant-design-vue组件写ant-design-vue-pro这个后台项目实现步骤的从零开始搭建的过程,视频地址,它采用了ant-desgin-vue的组件库作为素材开发, ...

  3. .NET面试题解析(9)-SQL语言基础及数据库基本原理

    见面试题 1. 索引的作用?她的优点缺点是什么? 2. 介绍存储过程基本概念和 她的优缺点? 3. 使用索引有哪些需要注意的地方? 4. 索引碎片是如何产生的?有什么危害?又该如何处理? 5. 锁的目 ...

  4. 控制台提示“Invalid string length”的原因

    控制台提示“Invalid string length”,浏览器直接卡掉,是为什么呢? 答:因为在写嵌套循环时,定义的变量重名了,内层和外层用了同一个i变量. -THE END-

  5. Computer: CMD and use windows system to better

    Xx_Introduction Please protection,respect,love,"China's Internet Security Act"! For learni ...

  6. 编译原理之非确定的自动机NFA确定化为DFA

    1.设有 NFA M=( {0,1,2,3}, {a,b},f,0,{3} ),其中 f(0,a)={0,1}  f(0,b)={0}  f(1,b)={2}  f(2,b)={3} 画出状态转换矩阵 ...

  7. 坚果云+typora(个人十分喜欢的一个记笔记方式)

    1.名称 坚果云 markdown--->typora 2.喜欢原因 2.1 坚果云 坚果云全平台覆盖,支持Windows.Mac.Linux.iOS(iPad及iPhone).Android. ...

  8. MySQL Error Log 文件丢失导致The server quit without updating PID file启动失败的场景

    今天在做mysql sniff测试的时候,中间重启MySQL实例的过程中,出现了"The server quit without updating PID file"这个经典的错误 ...

  9. 数据结构导论 四 线性表的顺序存储VS链式存储

    前几章已经介绍到了顺序存储.链式存储 顺序存储:初始化.插入.删除.定位 链式存储:初始化.插入.删除.定位 顺序存储:初始化 strudt student{ int ID://ID char nam ...

  10. Python连载54-FTP编程

    一.FTP工作流程 1.客户端链接远程主机上的FTP服务器 2.客户端输入用户名和密码(或者“anonymous”和电子邮件地址) 3.客户端和服务器进行各种文件传输和信息查询操作 4.客户端从远程F ...