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

本代码段对应流程4.1,连接池瘦身:


//连接池瘦身
public void shrink(boolean checkTime, boolean keepAlive) {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
return;
} int evictCount = 0;
int keepAliveCount = 0;
try {
if (!inited) {
return;
} final int checkCount = poolingCount - minIdle; //根据poolingCount和minIdle计算出evictCheck的范围
final long currentTimeMillis = System.currentTimeMillis();
for (int i = 0; i < poolingCount; ++i) { //开始遍历连接池里闲置的连接
DruidConnectionHolder connection = connections[i]; if (checkTime) { //除非手动调用,不然经过主流程4触发,一般为true
if (phyTimeoutMillis > 0) { //默认不启用,忽略
long phyConnectTimeMillis = currentTimeMillis - connection.connectTimeMillis;
if (phyConnectTimeMillis > phyTimeoutMillis) {
evictConnections[evictCount++] = connection;
continue;
}
} //计算闲置时间
long idleMillis = currentTimeMillis - connection.lastActiveTimeMillis; if (idleMillis < minEvictableIdleTimeMillis
&& idleMillis < keepAliveBetweenTimeMillis
) { //如果闲置时间达不到检测&瘦身的阈值,则不处理
break;
} if (idleMillis >= minEvictableIdleTimeMillis) {
if (checkTime && i < checkCount) { //达到需要丢弃的阈值时,则判断连接下标是否在evictCheck范围,若在,则视为“可以丢弃的对象”放入evictConnections数组
evictConnections[evictCount++] = connection;
continue;
} else if (idleMillis > maxEvictableIdleTimeMillis) { //达到必须要丢弃的阈值时,则不管是不是在evictCheck范围内,都直接放入“可以丢弃的对象”的evictConnections数组
evictConnections[evictCount++] = connection;
continue;
}
} //如果上面的条件均没有命中,如果keepAlive为true,则判断是不是超过了闲置连接检查其活性的频次阈值(即由keepAliveBetweenTimeMillis控制)
if (keepAlive && idleMillis >= keepAliveBetweenTimeMillis) {
keepAliveConnections[keepAliveCount++] = connection; //满足条件则视为“需要检测活性的对象”,放入keepAliveConnections数组
}
} else {
if (i < checkCount) {
evictConnections[evictCount++] = connection;
} else {
break;
}
}
} int removeCount = evictCount + keepAliveCount; //这一批需要移除特殊处理的连接总数
if (removeCount > 0) {
System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount); //根据当前移除的元素,把剩余的元素移动至数组首部(参考流程4.1)
Arrays.fill(connections, poolingCount - removeCount, poolingCount, null); //剩余位置清空
poolingCount -= removeCount;
}
keepAliveCheckCount += keepAliveCount;
} finally {
lock.unlock();
} if (evictCount > 0) { //如果需要丢弃的连接数量大于0
for (int i = 0; i < evictCount; ++i) {
DruidConnectionHolder item = evictConnections[i];
Connection connection = item.getConnection();
JdbcUtils.close(connection); //直接关闭连接(这里是直接关闭驱动连接,不再放回池子)
destroyCountUpdater.incrementAndGet(this);
}
Arrays.fill(evictConnections, null); //将evictConnections数组重新置空(方便下次使用)
} if (keepAliveCount > 0) { //检测那些需要判活的连接数
// keep order
for (int i = keepAliveCount - 1; i >= 0; --i) {
DruidConnectionHolder holer = keepAliveConnections[i];
Connection connection = holer.getConnection();
holer.incrementKeepAliveCheckCount(); boolean validate = false;
try {
this.validateConnection(connection); //检测其活性
validate = true;
} catch (Throwable error) {
if (LOG.isDebugEnabled()) {
LOG.debug("keepAliveErr", error);
}
// skip
} boolean discard = !validate;
if (validate) { //检测通过
holer.lastKeepTimeMillis = System.currentTimeMillis();
boolean putOk = put(holer); //检测通过后,再次放入池子
if (!putOk) { //放不进去池子(说明已经达到连接池最大连接数阈值maxActive),则视为可以“直接抛弃”的连接
discard = true;
}
} if (discard) {
try {
connection.close(); //如果可以抛弃,则直接关闭连接(直接调用驱动的close)
} catch (Exception e) {
// skip
} lock.lock();
try {
discardCount++; //抛弃连接数累加 if (activeCount <= minIdle) {
emptySignal(); //唤起主流程3追加连接对象
}
} finally {
lock.unlock();
}
}
}
this.getDataSourceStat().addKeepAliveCheckCount(keepAliveCount);
Arrays.fill(keepAliveConnections, null); //将keepAliveConnections数组重新置空(方便下次使用)
}
} //上面检测通过,再次通过该方法重新把连接放入池子
private boolean put(DruidConnectionHolder holder) {
lock.lock();
try {
if (poolingCount >= maxActive) {
return false; //若池子内闲置连接数超过maxActive,则无法继续添加新的连接进来,返回false
}
connections[poolingCount] = holder; //否则直接把此连接对象放入连接池队尾
incrementPoolingCount(); //poolingCount++ if (poolingCount > poolingPeak) {
poolingPeak = poolingCount;
poolingPeakTime = System.currentTimeMillis();
} notEmpty.signal(); //唤起那些因获取不到可用连接而陷入阻塞状态的业务线程一次
notEmptySignalCount++; if (createScheduler != null) { //不启用该模式,忽略
createTaskCount--; if (poolingCount + createTaskCount < notEmptyWaitThreadCount //
&& activeCount + poolingCount + createTaskCount < maxActive) {
emptySignal();
}
}
} finally {
lock.unlock();
}
return true;
}

Druid-代码段-4-2的更多相关文章

  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. 如何安装 IntelliJ IDEA 最新版本——详细教程

    IntelliJ IDEA 简称 IDEA,被业界公认为最好的 Java 集成开发工具,尤其在智能代码助手.代码自动提示.代码重构.代码版本管理(Git.SVN.Maven).单元测试.代码分析等方面 ...

  2. 如何让create-react-app锦上添花,满足实际需求?

    目录 前端部分 后端部分 create-react-app 是 React 官方为我们提供的一个单页应用脚手架,基于 webpack 配置了相关功能,babel. 图片处理.热加载.css 模块化.c ...

  3. css里的背景属性有哪些,如何去使用哪些属性

    分类:纯色背景    背景图像 1.背景颜色 background-color : 任意合法的颜色 和 transparent 2.背景图像 background-image : url(想要加载的图 ...

  4. JS 算数

    JS 算数 Math(算数)对象的作用是:执行常见的算数任务. random() 来返回 0 到 1 之间的随机数. max() 来返回两个给定的数中的较大的数.(在 ECMASCript v3 之前 ...

  5. android sdk tools里找不到draw9patch.bat 如何制作.9.png 格式图片

    1.什么是.9.png格式 .9.png格式的图片可以在安卓平台指定拉伸区域,防止图片失真,一般用作背景图. 2.前提 最近项目中要用到.9.png格式图片,想着这个简单使用draw9patch.ba ...

  6. CAS服务端数据源设置

    2.CAS服务端数据源设置 2.1需求分析 我们现在让用户名密码从我们的品优购的user表里做验证 2.2配置数据源 (1)修改cas服务端中web-inf下deployerConfigContext ...

  7. SQL Server 之事务执行,让语句在事务中执行

    BEGIN TRAN     BEGIN  TRY DELETE FROM dbo.表 INSERT INTO  dbo.表(    Id,   字段....) SELECTId,字段...    F ...

  8. centos7设置服务开机自启

    1.在/usr/lib/systemd/system/路径下创建文件 ***.service. 写入如下内容 [Unit]Description=nginxAfter=network.target [ ...

  9. html转换成canvas

    使用的工具是:html2canvas html2canvas(this.currentRef) .then(async (canvas) => { let url = canvas.toData ...

  10. Jenkins学习安装配置和使用

    为了能够频繁地将软件的最新版本,及时.持续地交付给测试团队及质量控制团队,以供评审,所以引入持续集成工具Jenkins,从而实现公司新产品持续集成,自动化部署. 环境准备 ●操作系统:Windows1 ...