Java并发编程实战
王宝令
资深架构师
立即订阅
15152 人已学习
课程目录
已完结 50 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 你为什么需要学习并发编程?
免费
学习攻略 (1讲)
学习攻略 | 如何才能学好并发编程?
第一部分:并发理论基础 (13讲)
01 | 可见性、原子性和有序性问题:并发编程Bug的源头
02 | Java内存模型:看Java如何解决可见性和有序性问题
03 | 互斥锁(上):解决原子性问题
04 | 互斥锁(下):如何用一把锁保护多个资源?
05 | 一不小心就死锁了,怎么办?
06 | 用“等待-通知”机制优化循环等待
07 | 安全性、活跃性以及性能问题
08 | 管程:并发编程的万能钥匙
09 | Java线程(上):Java线程的生命周期
10 | Java线程(中):创建多少线程才是合适的?
11 | Java线程(下):为什么局部变量是线程安全的?
12 | 如何用面向对象思想写好并发程序?
13 | 理论基础模块热点问题答疑
第二部分:并发工具类 (14讲)
14 | Lock和Condition(上):隐藏在并发包中的管程
15 | Lock和Condition(下):Dubbo如何用管程实现异步转同步?
16 | Semaphore:如何快速实现一个限流器?
17 | ReadWriteLock:如何快速实现一个完备的缓存?
18 | StampedLock:有没有比读写锁更快的锁?
19 | CountDownLatch和CyclicBarrier:如何让多线程步调一致?
20 | 并发容器:都有哪些“坑”需要我们填?
21 | 原子类:无锁工具类的典范
22 | Executor与线程池:如何创建正确的线程池?
23 | Future:如何用多线程实现最优的“烧水泡茶”程序?
24 | CompletableFuture:异步编程没那么难
25 | CompletionService:如何批量执行异步任务?
26 | Fork/Join:单机版的MapReduce
27 | 并发工具类模块热点问题答疑
第三部分:并发设计模式 (10讲)
28 | Immutability模式:如何利用不变性解决并发问题?
29 | Copy-on-Write模式:不是延时策略的COW
30 | 线程本地存储模式:没有共享,就没有伤害
31 | Guarded Suspension模式:等待唤醒机制的规范实现
32 | Balking模式:再谈线程安全的单例模式
33 | Thread-Per-Message模式:最简单实用的分工方法
34 | Worker Thread模式:如何避免重复创建线程?
35 | 两阶段终止模式:如何优雅地终止线程?
36 | 生产者-消费者模式:用流水线思想提高效率
37 | 设计模式模块热点问题答疑
第四部分:案例分析 (4讲)
38 | 案例分析(一):高性能限流器Guava RateLimiter
39 | 案例分析(二):高性能网络应用框架Netty
40 | 案例分析(三):高性能队列Disruptor
41 | 案例分析(四):高性能数据库连接池HiKariCP
第五部分:其他并发模型 (4讲)
42 | Actor模型:面向对象原生的并发模型
43 | 软件事务内存:借鉴数据库的并发经验
44 | 协程:更轻量级的线程
45 | CSP模型:Golang的主力队员
结束语 (1讲)
结束语 | 十年之后,初心依旧
用户故事 (2讲)
用户来信 | 真好,面试考到这些并发编程,我都答对了!
3 个用户来信 | 打开一个新的并发世界
Java并发编程实战
登录|注册

41 | 案例分析(四):高性能数据库连接池HiKariCP

王宝令 2019-06-01
实际工作中,我们总会难免和数据库打交道;只要和数据库打交道,就免不了使用数据库连接池。业界知名的数据库连接池有不少,例如 c3p0、DBCP、Tomcat JDBC Connection Pool、Druid 等,不过最近最火的是 HiKariCP。
HiKariCP 号称是业界跑得最快的数据库连接池,这两年发展得顺风顺水,尤其是 Springboot 2.0 将其作为默认数据库连接池后,江湖一哥的地位已是毋庸置疑了。那它为什么那么快呢?今天咱们就重点聊聊这个话题。

什么是数据库连接池

在详细分析 HiKariCP 高性能之前,我们有必要先简单介绍一下什么是数据库连接池。本质上,数据库连接池和线程池一样,都属于池化资源,作用都是避免重量级资源的频繁创建和销毁,对于数据库连接池来说,也就是避免数据库连接频繁创建和销毁。如下图所示,服务端会在运行期持有一定数量的数据库连接,当需要执行 SQL 时,并不是直接创建一个数据库连接,而是从连接池中获取一个;当 SQL 执行完,也并不是将数据库连接真的关掉,而是将其归还到连接池中。
数据库连接池示意图
在实际工作中,我们都是使用各种持久化框架来完成数据库的增删改查,基本上不会直接和数据库连接池打交道,为了能让你更好地理解数据库连接池的工作原理,下面的示例代码并没有使用任何框架,而是原生地使用 HiKariCP。执行数据库操作基本上是一系列规范化的步骤:
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java并发编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(23)

  • 空知
    线程本地的连接会被窃取
    这个我觉得是因为 如果 Tl里面没有空闲的 会去 sharedList查找处于 Not_In_Use的连接 这个连接可能已经在其他TL里面存在了 所以就会出现线程T2从sharedList获取到了 T1存在TL里面存放的没有使用的连接这种情况

    作者回复: 厉害

    2019-06-02
    11
  • 阿健
    同问,为什么说线程本地的连接会被窃取呢?
    2019-06-01
    8
  • 拯救地球好累
    支持高性能并发的软件通常首先会关注整体的并发设计模式,并发设计模式将影响整个软件的设计架构,比如RateLimiter并非采用较为复杂的生产者消费者模式,而是用细粒度的互斥锁来实现令牌桶算法;Netty采用了Reactor模式而非阻塞的等待-通知机制的一些实现。对设计模式的考量应当根据实际需求先考虑线程分工,再从避免共享的模式考虑到一些无锁的模式,再到细粒度的锁控制,再到复杂的同步和互斥模式。
    从高性能队列和高性能数据连接池中,可以看到,性能的提高通常会从几方面着手(实际场景中应当测试优于猜测,再根据阿姆达尔定律从性能瓶颈处先着手):并发设计模式;内存分配算法;缓存利用率;GC情况(有GC的语言);数据结构与算法的效率等。

    作者回复: 👍

    2019-08-10
    5
  • 晓杰
    同问为什么线程本地的会被其他线程窃取,麻烦老师解释一下

    作者回复: sharedlist和其他线程的threadlocal里有可能都有同一个连接,从前者取到连接,就相当于窃取了后者

    2019-06-02
    4
  • 沙漠里的骆驼
    窃取是在获取本地链接失败时,遍历sharelist实现的
    2019-06-02
    4
  • 想了半天感觉ConcurrentBag应该是池化的一种通用性优化,但好像会有饥饿问题,如果某些线程总是占用连接,那么某些不经常占用连接的就可能一直拿不到连接,硬想的一个缺点,哈哈哈。
    2019-06-01
    2
  • 银时空de梦
    最后数据库连接都到线程本地池中了
    2019-06-02
    1
    1
  • 张德
    强烈建议老师再讲一期

    作者回复: 呵呵😄

    2019-06-02
    1
  • cricket1981
    可以用栈stack来代替list实现逆序关闭S6~S1吗?
    2019-06-02
    2
    1
  • 往事随风,顺其自然
    bagEntry.setState(STATE_NOT_IN_USE);requilte方法里面写的有点问题吧,这个之前已经设置状态,后面for循环里面判断不等于未使用状态,这个多此一举
    2019-12-08
  • DFighting
    注意到了requite()的一个细节优化,自己使用完了线程后并不是直接交还给线程池,而是先问下有没有其他线程等待,如果有,那么直接分配就好,这里就减少了一个线程上下文切换带来的损失。不过这里的threadList应该只是会使用线程池的连接,不可以在这个连接上做一些自己数据的存储,因为如果这样就会给每次连接的归还时执行一次清洗工作,想来也会是一次性能的浪费吧。老师,关于连接池源码怎么看啊,像实践下今天课堂上学到的内容,但是不只如何下手

    作者回复: 先了解实现原理,然后写一个最简单的程序,调试跟代码。

    2019-10-16
  • neohope
    老师,您好,下面这一段没有看懂:
    else if ((i & 0xff) == 0xff) {
          parkNanos(MICROSECONDS.toNanos(10));
     }
    为什么需要这样一段呢?
    2019-08-18
  • 业余草
    看不上眼的优化,居然带来了巨大的性能提升。
    2019-07-28
  • 老王的老李头
    faststatementlist
    2019-06-14
  • Geek_89bbab
    threadList里面的连接可能也会存在于多个threadList,但是概率相对较小;threadList的连接的remove操作都由本线程来执行,窃取的线程只会把标识设置为已使用,而不会将其从对应的那个threadList移除。可能是为了避免多线程操作同一个队列,而影响性能。所以把移除threadList里的连接的任务交给对应的那个线程。
    2019-06-13
  • QQ怪
    根本看不够,强烈建议老师再来一篇

    作者回复: 我觉得可以开心地笑一下,然后,就没然后了😂😂😂

    2019-06-03
  • 星辰
    老师, 我看文中提到的是调用requite()释放链接的时候将这个链接添加到本地存储中。
    那我想问,如果不是调用requite()方法释放连接的情况下,这个连接第一次被放入threadlocal是什么时候啊? 是第一次获取连接的时候吗?

    作者回复: 只有requite的时候会放到threatlocal里

    2019-06-02
  • 张三
    打卡!
    2019-06-02
  • 龙猫
    需要多看几遍
    2019-06-02
  • 苏志辉
    这样会不会导致每个线程持有50个以下链接,而且每个链接可能在多个线程共存
    2019-06-01
收起评论
23
返回
顶部