⾼性能:池化技术的应⽤场景
若有收获,就点个赞吧
! SnailClimb
" 08-02 15:33
# 4547
$ 0
IP 属地湖北
举报
上⼀篇
⾼可⽤:负载均衡的常⻅算法有哪
些?
下⼀篇
⾼性能:零拷⻉为什么能提升性能?
注册 或 登录 语雀进⾏评论
加⼊语雀,参与知识分享与交流
或 语雀进⾏评论
注册
登录
⽴即加⼊
%
关于语雀 使⽤帮助 数据安全 服务协议 English 快速注册
简单来说,池化技术就是将可重复利⽤的对象⽐如连接、线程统⼀管理起来。线
程池、数据库连接池、HTTP、Redis 连接池等等都是对池化技术的应⽤。
通常来说,池化技术所管理的对象,⽆论是连接还是线程,它们的创建过程都⽐
较耗时,也⽐较消耗系统资源 。所以,我们把它们放在⼀个池⼦⾥统⼀管理起
来,以达到 提升性能和资源复⽤的⽬的 。
从上⾯对池化技术的介绍,我们可以得出池化技术的核⼼思想是空间换时间。它
的核⼼策略是使⽤已经创建好的对象来减少频繁创建对象的性能开销,同时还可
以对对象进⾏统⼀的管理。
不过,池化技术也不是并⾮没有缺点的。如果池⼦中的对象没有被充分利⽤的
话,也会造成多余的内存浪费(相对于池化技术的优点来说的话,这个缺点⼏乎
可以被忽略)。
线程池和数据库连接池我们平时开发过程中应该接触的⾮常多。因此,我会以线
程池和数据库连接池为例来介绍池化技术的实际应⽤。
正如其名,线程池主要负责创建和管理线程。
没有线程池的时候,我们每次⽤到线程就需要单独创建,⽤完了之后再销毁。然
⽽,创建线程和销毁线程是⽐较耗费资源和时间的操作。
有了线程池之后,我们可以重复利⽤已创建的线程降低线程创建和销毁造成的消
耗。并且,线程池还可以⽅便我们对线程进⾏统⼀的管理。
我们拿 JDK 1.5 中引⼊的原⽣线程池
ThreadPoolExecutor 来举例说明。
ThreadPoolExecutor 有 3 个最重要的参数:
corePoolSize : 核⼼线程数线程数定义了最⼩可以同时运⾏的线程数
量。
maximumPoolSize : 当队列中存放的任务达到队列容量的时候,当前可以
同时运⾏的线程数量变为最⼤线程数。
workQueue : 当新任务来的时候会先判断当前运⾏的线程数量是否达到核
⼼线程数,如果达到的话,新任务就会被存放在队列中。
假如我们需要提交任务给线程池执⾏的话,整个步骤是这样的:
1. 提交新任务
2. 判断线程池线程数是否少于
coreThreadCount ,是的话就创新线程处理
任务,否则的话就将任务丢到队列中等待执⾏。
3. 当队列中的任务满了之后,继续创建线程,直到线程数量达到
maxThreadC
ount 。
4. 当线程数量达到
maxThreadCount 还是有任务提交,那我们就直接按照拒
绝策略处理。
可以看出,JDK ⾃带的线程池
ThreadPoolExecutor 会优先将处理不过来的
任务放到队列中去,⽽不是创建更多的线程来处理任务。只有当队列中的等待执
⾏的任务满了之后,线程池才会创建线程,直到线程数达到
maximumPoolSize
。如果任务执⾏时间过⻓的话,还会很容易造成队列中的任务堆积。
并且,当线程数⼤于核⼼线程数时,如果线程等待 keepAliveTime 没有任务
处理的话,该线程会被回收,直到线程数缩⼩到核⼼线程数才不会继续对线程进
⾏回收。
可以看出,JDK ⾃带的的这个线程池
ThreadPoolExecutor ⽐较适合执⾏
CPU 密集型的任务,不太适合执⾏ I/O 密集型任务。
为什么这样说呢? 因此执⾏ CPU 密集型的任务时 CPU ⽐较繁忙,只需要创建
和 CPU 核数相当的线程就好了,多了反⽽会造成线程上下⽂切换。
如何判断是 CPU 密集任务还是 IO 密集任务? CPU 密集型简单理解就是利⽤
CPU 计算能⼒的任务⽐如你在内存中对⼤量数据进⾏排序。但凡涉及到⽹络读
取,⽂件读取这类都是 IO 密集型,这类任务的特点是 CPU 计算耗费时间相⽐于
等待 IO 操作完成的时间来说很少,⼤部分时间都花在了等待 IO 操作完成上。
在看极客时间的专栏《深⼊拆解 Tomcat & Jetty》的时候,我了解到:Tomcat
扩展了原⽣的 Java 线程池,来满⾜ Web 容器⾼并发的需求。
简单来说,Tomcat ⾃定义线程池继承了 JDK 线程池
java.util.concurrent
.ThreadPoolExecutor 重写了部分⽅法的逻辑(主要是 execute() ⽅
法)。Tomcat 还通过继承
LinkedBlockingQueue 重写 offer() ⽅法实
现了⾃定义的队列。
这些改变使得 Tomcat 的线程池在任务量⼤的情况下会优先创建线程,⽽不是直
接将不能处理的任务放到队列中。
Tomcat ⾃定义线程池的使⽤⽅法如下:
下⾯我们来详细看看 Tomcat 的线程池做了哪些改变。
Tomcat 的线程池通过重写
ThreadPoolExecutor 的 execute() ⽅法实现
了⾃⼰的任务处理逻辑。Tomcat 的线程池在线程总数达到最⼤时,不是⽴即执
⾏拒绝策略,⽽是再尝试向⾃定义的任务队列添加任务,添加失败后再执⾏拒绝
策略。那具体如何实现呢,其实很简单,我们来看⼀下 Tomcat 线程池的
exec
ute() ⽅法的核⼼代码。
到重点的地⽅了!Tomcat ⾃定义队列
TaskQueue 重写了 LinkedBlockingQ
ueue 的 offer ⽅法,这是关键所在!
当提交的任务数量⼤于当前的线程数的时候, offer() 会返回 false,线程池
会去创建新的线程,⽽不是等到任务队列满了之后再创建线程。
LinkedBlockingQueue 默认情况下⻓度是没有限制的,Tomcat ⾃定义队列
定义了⼀个
capacity 变量来限制队列⻓度。
TaskQueue 的 capacity 的默认值是 Integer.MAX_VALUE ,也就是说
默认情况下 Tomcat 的任务队列是没有⻓度限制的。不过,你可以通过设置
ma
xQueueSize 参数来限制任务队列的⻓度。
如果你想要获取更多关于线程的介绍的话,建议阅读我写的下⾯这⼏篇⽂章:
Java 线程池详解
Java 线程池最佳实践
数据库连接池属于连接池,类似于 HTTP、Redis 连接池,它们的实现原理类
似。连接池的结构示意图,如下所示(图⽚来⾃:《Java 业务开发常⻅错误
100 例》):
连接池负责连接的管理包括连接的建⽴、空闲连接回收等⼯作。
我们这⾥以数据库连接池为例来详细介绍。
没有数据库线程池之前,我们接收到⼀个需要⽤到数据库的请求,通常是这样来
访问数据库的:
1. 装载数据库驱动程序;
2. 通过 JDBC 建⽴数据库连接;
3. 访问数据库,执⾏ SQL 语句;
4. 断开数据库连接。
假如我们为每⼀个请求都建⽴⼀次数据库连接然后再断开连接是⾮常耗费资源和
时间的。因为,建⽴和断开数据库连接本身就是⽐较耗费资源和时间的操作。
如果我们频繁进⾏数据库连接的建⽴和断开操作的话,势必会影响到系统的性
能。当请求太多的话,系统甚⾄会因为创建太多数据库连接⽽直接宕机。
因此,有了数据库连接池来管理我们的数据库连接。当有请求的时候,我们现在
数据库连接池中检查是否有空闲的数据库连接,如果有的话,直接分配给它。
如果我们需要获取数据库连接,整个步骤是这样的:
1. 系统⾸先检查空闲池内有没有空闲的数据库连接。
2. 如果有的话,直接获取。
3. 如果没有的话,先检查数据库连接池的是否达到所允许的最⼤连接数,没达
到的话就新建⼀个数据库连接,否则就等待⼀定的时间(timeout)看是否
有数据库连接被释放。
4. 如果等待时间超过⼀定的时间(timeout)还是没有数据库连接被释放的
话,就会获取数据库连接失败。
实际开发中,我们使⽤ HikariCP 这个线程的数据库连接池⽐较多,SpringBoot
2.0 将它设置为默认的数据源连接池。
HikariCP 为了性能的提升(号称是史上性能最好的数据库连接池),做了⾮常多
的优化,⽐如 HikariCP ⾃定义
FastStatementList 来代替 ArrayList 、
⾃定义
ConcurrentBag 来提⾼并发读写的效率,再⽐如 HikariCP 通过
Javassist 来优化并精简字节码。
想要继续深⼊了解 HikariCP 原理的⼩伙伴,可以看看下⾯这两篇⽂章:
SpringBoot 2.0 中 HikariCP 数据库连接池原理解析 - vivo 互联⽹技术
HikariCP 的这波优化,太炸裂了!
HikariCP 是性能超强,在监控⽅⾯的话,数据库连接池 Druid 做的不错。
池⼦的最⼤值和最⼩值的设置很重要,初期可以依据经验来设置,后⾯还是
需要根据实际运⾏情况做调整。
池⼦中的对象需要在使⽤之前预先初始化完成,这叫做池⼦的预热,⽐⽅说
使⽤线程池时就需要预先初始化所有的核⼼线程。如果池⼦未经过预热可能
会导致系统重启后产⽣⽐较多的慢请求。
《Java 业务开发常⻅错误 100 例:04 | 连接池:别让连接池帮了倒忙》
《深⼊拆解 Tomcat & Jetty》:17 | Executor 组件:Tomcat 如何扩展
Java 线程池?
池化技术简介
池化技术常⻅应⽤
线程池
●
●
●
线程池
ThreadPoolExecutor
不是上来就是直接初始化
corePoolSize
个线程,⽽是有任务来了才创建线程处理任务。
●
●
数据库连接池
●
●
池化技术注意事项
●
●
参考
●
●
⼤纲
池化技术简介
!
池化技术常⻅应⽤
线程池
数据库连接池
池化技术注意事项
参考
⾼性能:池化技术的应⽤场景
登录 / 注册
《Java⾯试指北》
⽬录
系统设计
Java
数据库
常⻅框架
分布式
⾼并发
⾼可⽤:如何设计…
⾼可⽤:负载均衡…
⾼性能:池化技术…
⾼性能:零拷⻉为…
⾼性能:有哪些常…
⾼可⽤:降级和熔…
⾼可⽤:灰度发布…
服务器
Devops
技术⾯试题⾃测篇
itjc8.com搜集整理