「本文已参与 周末学习计划 ,点击查看详情」
前言
池化技术是一种很常见的编程技巧,在请求并发很大时能明显优化应用性能,降低系统频繁创建链接的资源开销。
常见的有数据库连接池、线程池、对象池等,它们的特点都是将可以复用的东西维护在一个特定的池子中,规定其最小连接数、最大连接数、阻塞队列等配置,方便进行统一管理和复用,通常还会附带一些探活机制、强制回收、监控一类的配套功能。
线程池
JDK 1.5 中引入的 ThreadPoolExecutor 就是一种线程池的实现,它有两个重要的参数: corePoolSize 和 maximumPoolSize ,这两个参数控制着线程池的执行过程。
它的执行原理如下:
- 通过
execute()提交任务时,当线程池中的线程数小于corePoolSize时,新提交的任务将通过创建一个新线程来执行,即使此时线程池中存在空闲线程。 - 通过
execute()提交任务时,当线程池中线程数量达到corePoolSize时,新提交的任务将被放入workQueue中,等待线程池中线程调度执行。 - 通过
execute()提交任务时,当workQueue已存满,且maximumPoolSize大于corePoolSize时,新提交的任务将通过创建新线程执行。 - 当线程池中的线程执行完任务空闲时,会尝试从
workQueue中取头结点任务执行。 - 通过
execute()提交任务,当线程池中线程数达到maxmumPoolSize,并且workQueue也存满时,新提交的任务由RejectedExecutionHandler执行拒绝操作。 - 当线程池中线程数超过
corePoolSize,并且未配置allowCoreThreadTimeOut=true,空闲时间超过keepAliveTime的线程会被销毁,保持线程池中线程数为corePoolSize。 - 当设置
allowCoreThreadTimeOut=true时,任何空闲时间超过keepAliveTime的线程都会被销毁。
动态线程池
可以看看这篇Java线程池实现原理及其在美团业务中的实践,你会很有启发的。
数据库连接池
此处以 Spring 默认数据库连接池 HikariCP 为例:
maximum-pool-size: 连接池中最大连接数(包括空闲和正在使用的连接)默认值是 10。minimum-idle: 池中最小空闲连接数量,默认值 10,小于池中最大连接数。pool-name: 连接池的名字。auto-commit: 是否自动提交池中返回的连接。idle-timeout: 空闲时间。max-lifetime: 连接池中连接的最大生命周期。connection-timeout: 连接超时时间。
连接策略:
- 小于
minimum-idle: 如果当前连接数小于最小连接数,则创建新的连接处理数据库请求;如果连接池中有空闲连接则复用空闲连接; - 大于
minimum-idle并且小于maximum-pool-size: 如果空闲池中没有连接并且当前连接数小于最大连接数,则创建新的连接处理请求; - 大于等于
maximum-pool-size: 如果当前连接数已经大于等于最大连接数,则按照配置中设定的时间等待旧的连接可用; - 如果等待超过了这个设定时间则向用户抛出错误。
对象池
平时大家都是直接 new 一个对象,但是对于一些大对象、构造耗时较多的对象这样不是一个好方法,因此我们就可以使用对象池来避免上述导致的问题。
为了避免重复造轮子,采用 Apache commons-pool 来实现,ObjectPool 主要的方法如下:
borrowObject(): 从对象池中获取对象的方法。returnObject(T obj): 将对象返还给对象池。invalidateObject(T obj): 让对象失效。addObject(): 往对象池中新增一个对象。getNumIdle(): 获取当前闲置在对象池中的对象数量,即没有被拿走使用的对象数量。getNumActive(): 获取已经在使用中的对象数量,即被使用者从对象池中拿走使用的数量。clear(): 清空对象池中闲置的所有对象。close(): 关闭对象池。
PooledObject 抽象了对象池中对象应该具备的一些属性。注意,这个对象并不是我们真正要存的对象,而是经过一层封装的对象,那么真正存放在对象池的其实都是经过封装过的对象,即 PooledObject 对象。
总结
上面的动态线程池让我有点思考,是不是可以通过字节码增强技术来隐式的强化线程池的功能,对客户端完全无感调用。
个人备注
此博客内容均为作者学习所做笔记,侵删! 若转作其他用途,请注明来源!