前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >九、HikariCP源码分析之ConcurrentBag二

九、HikariCP源码分析之ConcurrentBag二

原创
作者头像
用户1422411
发布于 2022-06-25 09:55:36
发布于 2022-06-25 09:55:36
4130
举报

欢迎访问我的博客,同步更新: 枫山别院

源代码版本2.4.5-SNAPSHOT

②检查本地保存的连接

代码语言:java
AI代码解释
复制
//②
//如果ThreadLocal中有连接的话, 就遍历, 尝试获取
//从后往前反向遍历是有好处的, 因为最后一次使用的连接, 空闲的可能性比较大, 之前的连接可能会被其他线程偷窃走了
for (int i = list.size() - 1; i >= 0; i--) {
  final Object entry = list.remove(i);
  @SuppressWarnings("unchecked") final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
  if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
     return bagEntry;
  }
}

OK,我们继续。这里遍历的 list 变量,是threadList,是当前线程使用过的连接,保存在本地线程的引用。

可以看到这个遍历的顺序,是倒序遍历的。list 里的连接,肯定是最后一个是最后放进去的,也就是最近使用过的,这个连接还可以继续使用的可能极大,时间越早的连接,就越可能被其他线程借用走了,所以这就是为什么要倒序遍历,我们要先检查能使用的可能性最大的连接。说到这里,我们在平时的业务代码中,要用 if 检查一些条件,这时候我们要有意识的先检查可能性最大的条件,这有利于减少判断的次数,提高程序的性能。

在从 list 中获取连接的时候,使用的是remove方法,也就是说,无论如何,这个连接的引用我们不在本地保存了,如果它可以用,那么用完之后它又会加入到本地的threadList,如果不能使用了,那么我们就删除了这个无用的连接引用。

下面第 三句代码其实就是类型的强转,忽略之。有意思的是接下来第四句的判断,直接在 if 中就执行修改连接状态的操作。每个连接都有一个状态,它的类型是AtomicInteger,是个数字,并且是原子操作,线程安全。compareAndSet方法执行的时候,将STATE_NOT_IN_USE状态跟连接的当前状态对比,一样的时候才将它修改成STATE_IN_USE,既保证了线程安全,又保证了只有在连接是空闲状态才能使用线程,不会错误使用了其他状态的连接。此时,如果状态修改成功了,那么直接将该连接返回给用户使用。

说到这里,应该说一下连接有哪些状态。

连接的状态

  • STATE_NOT_IN_USE = 0 //空闲状态, 可以被借走
  • STATE_IN_USE = 1 //使用中状态, 已经被借走, 正在使用中
  • STATE_REMOVED = -1 //已删除状态, 不能借走
  • STATE_RESERVED = -2 //保留状态, 不能借走

一共四种状态,只有在连接空闲的时候才能被借走,其他三个状态都不行。值得一提的是STATE_RESERVED状态,它是在连接池挂起时的一个状态,如果不知道连接池挂起,大家可以看下《HikariCP源码分析之获取连接流程二》。

③准备工作

代码语言:java
AI代码解释
复制
//③
//如果没有从ThreadLocal中获取到连接, 那么就sharedList连接池中遍历, 获取连接, timeout时间后超时
//因为ThreadLocal中保存的连接是当前线程使用过的, 才会在ThreadLocal中保留引用, 连接池中可能还有其他空闲的连接, 所以要遍历连接池
//看一下requite(final T bagEntry)方法的实现, 还回去的连接放到了ThreadLocal中
timeout = timeUnit.toNanos(timeout);
Future<Boolean> addItemFuture = null;
//记录从连接池获取连接的开始时间, 后面用
final long startScan = System.nanoTime();
final long originTimeout = timeout;
long startSeq;
//将等待连接的线程计数器加 1
waiters.incrementAndGet();

上面说到了从本地的threadList中获取连接,如果拿不到的话,那么就要走到这里了。这里是获取连接前的准备工作。

  • timeout是获取连接的超时时间,这个是作为参数传入的,转换为纳秒可以提高精度。
  • startScan是记录开始获取连接的起始时间,用于后面计算还剩下多少时间的。
  • originTimeout就等于timeout,因为timeout代表剩余时间,后面的计算中会变的,所以赋值给originTimeout,记住原始的值是多少。
  • startSeq用于记录当期synchronizer的值,它是判断是否有可用连接加入连接池的。每当有一个可用连接加入,synchronizer就会加 1,我们只要记下synchronizer的当前值,然后一段时间后比较synchronizer最新值,如果synchronizer变大了,就说明有新连接加入了。
  • waiters是等待中的线程数,是记录有多少线程在等待获取连接的计数器。此处将计数器加 1。

其实上面代码都是一些用于记录原始值的,没什么好说的。

我们稍微说说System.nanoTime()吧,跟System.currentTimeMillis()非常像的方法,都是获取时间的。但是System.currentTimeMillis()获取的是系统时间的毫秒数,而System.nanoTime()获取的并不是系统时间的纳秒数,这个很多同学可能一直误会了这个方法。

JDK 提供的System.currentTimeMillis()方法其实获取的时间并不准确,因为可能会受到时间校准的影响,而System.nanoTime()返回当前JVM的高精度时间,该方法只能用来测量时段而和系统时间无关,它的返回值可能是从某个时间点开始计算的,可能是负数,只能用于计算时间差,不能用于系统时间相关的逻辑。

所以,在做性能测试的时候,统计时间,使用System.nanoTime()比较好。Netty 中的时间轮就是使用这个,调系统时间不会导致触发任务。

④⑤添加连接任务

代码语言:java
AI代码解释
复制
do {
//④
//当前连接池中的连接数, 在连接池中有新的可用连接的时候, 该值会增加
startSeq = synchronizer.currentSequence();
for (T bagEntry : sharedList) {
   if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
      // if we might have stolen another thread's new connection, restart the add...
      //⑤
      //如果waiters大于 1, 说明除了当前线程之外, 还有其他线程在等待空闲连接
      //这里, 当前线程的addItemFuture是 null, 说明自己没有请求创建新连接, 但是拿到了连接, 这就说明是拿到了其他线程请求创建的连接, 这就是所谓的偷窃了其他线程的连接, 然后当前线程请求创建一个新连接, 补偿给其他线程
      if (waiters.get() > 1 && addItemFuture == null) {
         //提交一个异步添加新连接的任务
         listener.addBagItem();
      }
      return bagEntry;
   }
}
} while (startSeq < synchronizer.currentSequence()); //如果连接池中的空闲连接数量比循环之前多了, 说明有新连接加入, 继续循环获取

④和⑤我们还是一起分析比较好。上面我们已经说过了startSeq,略。

sharedList是整个 HikariCP 的连接池,里面保存了所有的连接,终于,在这里进入主题了。

我们遍历整个连接池,尝试获取连接,在if 的compareAndSet中一个一个尝试修改它的状态,如果修改成功了,说明我们拿到了这个连接的使用权,Good!本来应该直接 return这个连接给用户就可以了吧?为什么还要判断???

在上面一篇文章中,我们举例租车的时候,提到过,线程间的连接是会相互窃取的,其实那个窃取不算是真的窃取,因为虽然你本地保存了连接的引用,但是连接又不是你创建的,其他线程也可以从连接池里拿,没有毛病。

但是这里是真的窃取。我们判断waiters.get() > 1 && addItemFuture == nullwaiters是当前正在等待获取连接的线程数,这个我们说过了。它大于 1,说明除了当前线程自己在等之外,还有别的线程也在等连接呢;addItemFuture代表创建连接的任务,它是 null 的话,说明当前线程自己没有创建过创建新连接的任务。但是呢,我居然拿到连接了,你说运气好不好!巧了呀!既然这个连接不是我们创建的,那肯定是别的线程创建的呀,我们偷来了,这咋整呢,要不我们补偿一个给它吧。于是,我们执行listener.addBagItem();请求创建一个新连接补偿给别的线程,别让人一直等了!

如果 for 循环执行完了,还是没有拿到连接呢?这个 for 循环是 do-while 循环中嵌套的 for 循环,for 循环执行完了一遍,就说明整个连接池我们查找了一遍,没有拿到连接。那么do-while要不要继续执行,要看条件了对吧?

这里startSeq < synchronizer.currentSequence()startSeq是我们循环之前记录的连接数量,synchronizer.currentSequence()是当前的连接数量,如果现在比之前的数量大了,说明有新的可用连接加入了连接池,就可以继续执行 for 循环遍历连接池。

如果startSeq < synchronizer.currentSequence()不成立,说明我们在执行 for 循环期间,没有新连接加入连接池。

⑥请求创建连接

代码语言:java
AI代码解释
复制
//⑥
//循环完一遍连接池(也可能循环多次, 如果正好在第一次循环完连接池后有新连接加入, 那么会继续循环), 还是没有能拿到空闲连接, 就请求创建新的连接
if (addItemFuture == null || addItemFuture.isDone()) {
addItemFuture = listener.addBagItem();
}
//计算 剩余的超时时间 = 用户设置的connectionTimeout - (系统当前时间 - 开始获取连接的时间_代码①处 即从连接池中获取连接一共使用的时间)
timeout = originTimeout - (System.nanoTime() - startScan);
} while (timeout > 10_000L && synchronizer.waitUntilSequenceExceeded(startSeq, timeout)); //③

上面的代码是不完整的,少了一个 do,在最外层,不影响我们分析。此处的循环是一个三层的嵌套,两个 do-while,里面再嵌套一个 for 循环,很绕。

上面我们说到了,遍历完整个连接池之后,也没有新连接加入连接池,for 循环和里层的 do-while 就执行完了。此时是真的没有连接可用了,怎么办,创建一个连接呗,然后执行如下的 if判断addItemFuture == null || addItemFuture.isDone()addItemFuture == null说明没有创建过创建连接的任务,那么addItemFuture.isDone()是啥?这是说明它创建过创建连接的任务,任务执行完了,悲催的是,我们还是没有拿到创建的连接,肯定是让哪个天杀的线程偷了,没有天理了呀!没办法,我们执行addItemFuture = listener.addBagItem();再创建一个连接。

此时,还有最外层的一个 do-while 循环,它的判断条件是什么?我们好像还一直没有检查超时时间吧?没错,就是这个了。timeout = originTimeout - (System.nanoTime() - startScan);计算一下,我们获取连接还剩余多少时间了。

timeout > 10_000L && synchronizer.waitUntilSequenceExceeded(startSeq, timeout)这个条件有点小复杂。

  • timeout > 10_000L表示剩余时间至少要大于10000纳秒,太少了说不定还没有执行一次 for 循环就到时间了。
  • synchronizer.waitUntilSequenceExceeded(startSeq, timeout)这个条件比较神奇,如果在timeout时间内,synchronizer的值大于startSeq,就返回 true。

综合一下,最外层的 do-while 要执行的话,必须剩余超时时间timeout大于 10000 纳秒,同时,在timeout时间用尽之前,必须有新连接加入连接池,我们才能继续执行循环,否则一直阻塞,直到条件达成或者时间用尽。

最终结果

代码还剩一个 finally 了。

代码语言:java
AI代码解释
复制
} finally {
      waiters.decrementAndGet();
   }

   return null;

borrow 方法的最后几行代码,直到线程前面用尽最后的时间,也没有获取到连接,最后,不能再等了,从等待线程的计数器中把自己减去,直接返回 null 给用户吧,尽力了......

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
中国版Cursor | 我用CodeBuddy Craft 3分钟复刻NFC经典游戏
做为一个懒人程序员,从AI迅速发展开始就一直在寻找能够替代码农编码的AI工具。从最早的GitHub Copilot,到CodeGeeX,再到Bito。紧接着后面的Cursor,Trae等等。技术生态发展之迅猛,让牛马们直呼学不完。
有一只柴犬
2025/05/13
1761
中国版Cursor | 我用CodeBuddy Craft 3分钟复刻NFC经典游戏
腾讯云代码助手 CodeBuddy初体验
大家好,我是杜金房,是一个老码农。今天跟大家聊聊AI辅助编程——我使用CodeBuddy的体验。
杜金房
2025/05/13
2810
腾讯云代码助手 CodeBuddy初体验
VS Code一秒生成80%代码?鹅厂人真实感受
# 第3期 | 腾讯孟健:测评 VSCode Copilot Chat 新特性体验
腾讯云开发者
2023/08/13
9060
VS Code一秒生成80%代码?鹅厂人真实感受
Jupyter AI:通过聊天生成代码、修改错误,支持各种大模型
“ Jupyter Lab 的GitHub Copilot、Cursor:免费开源的智能开发插件 Jupyter AI。”
技术人生黄勇
2024/07/19
4330
Jupyter AI:通过聊天生成代码、修改错误,支持各种大模型
【超级助力】腾讯云 AI 代码助手 + DeepSeek :小程序轻松开发!
Visual Studio Code 、JetBrains IDEs、Visual Studio 等
是山河呀
2025/02/22
3920
微软官宣All in智能体,SWE Agent首曝光!奥特曼预警2025编程巨变
就在今天,纳德拉官宣,GitHub Copilot将all-in智能体,微软自主的SWE智能体首次亮相。
新智元
2025/02/15
990
微软官宣All in智能体,SWE Agent首曝光!奥特曼预警2025编程巨变
使用Trae AI IDE,轻松实现交友聊天功能
在软件开发过程中,需求分析、代码编写和调试优化是三个关键环节,对于开发者来说,时间就是金钱,但是在实际开发过程中,这些环节往往存在效率瓶颈。需求分析、代码编写和调试优化往往耗时过长,比如在需求分析阶段,我们需要与产品经理、设计师反复沟通,梳理需求并制定开发计划方案;又如在代码编写阶段的时候,复杂的业务逻辑和大量的重复代码让大家疲惫不堪,非常“无奈”;再如在调试优化阶段,频繁的错误排查和性能优化更是令人头疼,不仅浪费时间,还可能引入新的问题,尤其是遇到一些“无厘头”的问题。个人觉得,这些痛点不仅降低了开发效率,还增加了项目的交付风险,也是程序员日常工作中严重影响开发效率的“拦路虎”。
三掌柜
2025/02/26
6512
使用Trae AI IDE,轻松实现交友聊天功能
集成GPT-4的编程神器来了,GitHub发布Copilot X:编程30年,突然就不需要手敲代码了?!
作者 | 核子可乐、Tina 畅想十多年后,失去了“AI 助手”的年轻人无法编码了,那企业只能再高薪聘请真正会编程的白发老年人回来了..... 在 GPT-4 模型的支持下,微软打造的 AI“结对编程助手”已经能够标记 pull 请求、生成模仿文档和讨论代码内容。GitHub 还添加了 OpenAI 的聊天功能,这也就是说 Copilot X 将允许程序员以 ChatGPT 的风格,通过语音聊天来编写和调试代码。 1 GPT-4 驱动的新 Copilot 编码助手 经过新一轮训练,微软 GitHu
深度学习与Python
2023/03/29
2.4K0
集成GPT-4的编程神器来了,GitHub发布Copilot X:编程30年,突然就不需要手敲代码了?!
GPT-4 Copilot X震撼来袭!AI写代码效率10倍提升,码农遭降维打击
---- 新智元报道   编辑:编辑部 【新智元导读】GPT-4加强版Copilot来了!刚刚,GitHub发布了新一代代码生成工具GitHub Copilot X,动嘴写代码不再是梦。 微软真的杀疯了! 上周,微软刚用GPT-4升级了Office办公全家桶,还没等人们反应过来,微软又来抄码农的家了。 今天,GitHub官宣:基于GPT-4的新一代代码生成工具Copilot X来了! GitHub Copilot发布还不到两年,就已经为100多万的开发者编写了46%的代码,并提高了55%的编码速度。
新智元
2023/03/29
1.3K0
GPT-4 Copilot X震撼来袭!AI写代码效率10倍提升,码农遭降维打击
AI自动编码将上线!谷歌Colab硬刚Github Copilot,编程效率要翻天
---- 新智元报道   编辑:润 好困 【新智元导读】谷歌Colab即将上线大模型辅助编程,代码生成,代码补全,聊天机器人全都有。 谷歌版的Github Copilot来了! 5月17日,谷歌宣布,Google Colaboratory(Colab)即将加入全新的AI编码功能—— 代码生成,代码补全,代码聊天机器人,你能想到的全都有。 再加上Colab的之前提供的全云端运行的Jupyter笔记本环境,开发者可以方便使用Keras,TensorFlow,PyTorch,OpenCV等框架在谷歌提供的
新智元
2023/05/22
4830
AI自动编码将上线!谷歌Colab硬刚Github Copilot,编程效率要翻天
在Win上做Python开发?当然是用官方的MS Terminal和VS Code了
使用 Windows 系统一大好处是它的应用太丰富了,甚至强大的 GPU 也能在闲暇时间做点其它「工作」。然而与 Linux 或 macOS 不同,在 Windows 上做开发总会遇到很多挑战,不论是文件编码、环境控制还是项目编译,开发过程中总会有一些神奇的收获。
机器之心
2019/08/26
5K0
在Win上做Python开发?当然是用官方的MS Terminal和VS Code了
JetBrains推出本地运行的AI代码补全
人工智能驱动的全新代码补全工具旨在将代码保留在本地,从而降低受监管行业的安全性问题。
云云众生s
2024/04/09
4510
微软AI宇宙日益完善!ChatGPT默认用必应搜索,Windows Copilot登场!
微软在5月23日举办的微软 Build 2023 大会上宣布了一则令人振奋的消息。毫无疑问,本次大会的重点是AI,包括面向Windows 11的Copilots和其他一系列产品。
AiCharm
2023/06/07
3960
微软AI宇宙日益完善!ChatGPT默认用必应搜索,Windows Copilot登场!
机器学习新手必看:Jupyter Notebook入门指南
翻译 | 张建军 出品 | 人工智能头条(公众号ID:AI_Thinker) 【人工智能头条导读】Jupyter Notebook 是一个 Web 应用程序,便于创建和共享文学化程序文档,支持实时代码、数学方程、可视化和 Markdown,其用途包括数据清理和转换、数值模拟、统计建模、机器学习等等。目前,数据挖掘领域中最热门的比赛 Kaggle 里的资料都是 Jupyter 格式。对于机器学习新手来说,学会使用 Jupyter Notebook 非常重要。 下面这篇 Jupyter Notebook 入门指
用户1737318
2018/06/05
3K0
北大团队搞出ChatExcel,说人话自动处理表格,免费且不限次使用
来源:量子位 | 公众号 QbitAI 做Excel表,真就动动嘴就够了! 看,输入想要干的事:给学生成绩排个名吧。 简单敲个回车,表格唰一下就列好了! 检查一遍也没错。 还能跨表格处理。 比如标记出两张不同表格中排名都在前十的学校。 哦豁,还有点超越ChatGPT? 毕竟给ChatGPT提出类似的要求,它只能帮我写出对应的代码,操作还得自己手动来。 这就是最近在知乎上引起关注的AI工具ChatExcel,一发布就登上了热榜。 它主打用对话的形式实现Excel复杂操作(Chat-to-Excel)
程序猿DD
2023/04/04
5000
北大团队搞出ChatExcel,说人话自动处理表格,免费且不限次使用
游戏开发者使用code buddy能做成什么样? C# Unity
其实,腾讯的code buddy (下文简称为buddy) 和现在大多数的AI辅助编程一样,都是帮助各位IT行业的小伙伴更加高效的编辑代码(make your life better ),目前是免费的阶段(**个人版免费** 企业版与专业版收费详见官网).使用方法与cursor类似.在代码编辑器扩展中安装,然后登陆账号.即可开始使用.使用是很简单的,但是如何去用,也是一门学问.
张曙光
2025/05/13
830
游戏开发者使用code buddy能做成什么样? C# Unity
AI 加持的代码编写实战:快速实现 Nginx 配置格式化工具
本篇文章聊聊如何使用 GPT 快速完成一个开源小项目,解决实际的问题,顺手点亮 GitHub 上 Nginx 开源社区的贡献者图标。
soulteary
2023/05/20
9630
GPT学术优化 (GPT Academic):支持一键润色、一键中英互译、一键代码解释、chat分析报告生成、PDF论文全文翻译功能、互联网信息聚合+GPT等等
ChatGPT/GLM提供图形交互界面,特别优化论文阅读/润色/写作体验,模块化设计,支持自定义快捷按钮&函数插件,支持Python和C++等项目剖析&自译解功能,PDF/LaTex论文翻译&总结功能,支持并行问询多种LLM模型,支持清华chatglm等本地模型。兼容复旦MOSS, llama, rwkv, 盘古, newbing, claude等
汀丶人工智能
2023/10/11
2.8K0
盘一盘可以给开发人员提效的人工智能/AI开发者工具
这是一个精选的人工智能开发者工具列表。这些工具利用人工智能来辅助开发人员完成代码补全、重构、调试、文档编写等任务。
山行AI
2023/09/08
7270
盘一盘可以给开发人员提效的人工智能/AI开发者工具
80%代码秒生成!AI神器Copilot大升级,百万开发者动嘴编码5年内成真
就在刚刚,Github官宣,Copilot模型升级,5年内80%的代码将自动生成。
新智元
2023/08/07
5850
80%代码秒生成!AI神器Copilot大升级,百万开发者动嘴编码5年内成真
推荐阅读
中国版Cursor | 我用CodeBuddy Craft 3分钟复刻NFC经典游戏
1761
腾讯云代码助手 CodeBuddy初体验
2810
VS Code一秒生成80%代码?鹅厂人真实感受
9060
Jupyter AI:通过聊天生成代码、修改错误,支持各种大模型
4330
【超级助力】腾讯云 AI 代码助手 + DeepSeek :小程序轻松开发!
3920
微软官宣All in智能体,SWE Agent首曝光!奥特曼预警2025编程巨变
990
使用Trae AI IDE,轻松实现交友聊天功能
6512
集成GPT-4的编程神器来了,GitHub发布Copilot X:编程30年,突然就不需要手敲代码了?!
2.4K0
GPT-4 Copilot X震撼来袭!AI写代码效率10倍提升,码农遭降维打击
1.3K0
AI自动编码将上线!谷歌Colab硬刚Github Copilot,编程效率要翻天
4830
在Win上做Python开发?当然是用官方的MS Terminal和VS Code了
5K0
JetBrains推出本地运行的AI代码补全
4510
微软AI宇宙日益完善!ChatGPT默认用必应搜索,Windows Copilot登场!
3960
机器学习新手必看:Jupyter Notebook入门指南
3K0
北大团队搞出ChatExcel,说人话自动处理表格,免费且不限次使用
5000
游戏开发者使用code buddy能做成什么样? C# Unity
830
AI 加持的代码编写实战:快速实现 Nginx 配置格式化工具
9630
GPT学术优化 (GPT Academic):支持一键润色、一键中英互译、一键代码解释、chat分析报告生成、PDF论文全文翻译功能、互联网信息聚合+GPT等等
2.8K0
盘一盘可以给开发人员提效的人工智能/AI开发者工具
7270
80%代码秒生成!AI神器Copilot大升级,百万开发者动嘴编码5年内成真
5850
相关推荐
中国版Cursor | 我用CodeBuddy Craft 3分钟复刻NFC经典游戏
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档