Apache Commons Pool2 池化技术
对象池是一种设计模式,用于管理和重用对象,以提高性能和资源利用率。对象池的概念在许多应用程序中都有广泛应用,特别是在需要频繁创建和销毁对象的情况下,例如数据库连接、线程、HTTP连接等
对象池通过预先创建一组对象并将它们存储在池中,以供需要时获取和使用。当对象不再需要时,它们不会被销毁,而是被返回到池中,以便在后续的请求中重新使用
对象池的优点包括:
- 减少对象创建和销毁的开销,提高性能和响应时间
- 最大限度地利用系统资源,避免资源的浪费
- 控制对象的数量,防止资源耗尽和系统崩溃
- 提供对象的重用机制,避免频繁的对象创建和垃圾回收
Apache Commons Pool2
Apache Commons Pool2 是一个广泛使用的Java对象池库,被许多开源项目和企业应用程序采用。它提供了通用的对象池化解决方案,可用于管理各种类型的对象,如数据库连接、线程池、HTTP连接等
Commons Pool 工作原理
核心类
ObjectPool
对象池,负责对对象进行生命周期的管理,并提供了对对象池中活跃对象和空闲对象统计的功能
- 对象的提供与归还:
borrowObject
、returnObject
- 创建对象:
addObject
- 销毁对象:
invalidateObject
- 池中空闲对象数量、被使用对象数量:
getNumActive
、getNumIdle
PooledObjectFactory
对象工厂类,负责具体对象的创建、初始化,对象状态的销毁和验证
commons-pool2框架本身提供了默认的抽象实现
BasePooledObjectFactory
,业务方在使用的时候只需要继承该类,然后实现warp
和create
方法即可
PooledObject
池化对象,是需要放到ObjectPool对象的一个包装类。添加了一些附加的信息,比如说状态信息,创建时间,激活时间等
池对象状态及流程
PooledObjectState
池对象状态枚举
public enum PooledObjectState {//在空闲队列中,还未被使用IDLE,//使用中ALLOCATED,//在空闲队列中,当前正在测试是否满足被驱逐的条件EVICTION,//不在空闲队列中,目前正在测试是否可能被驱逐。因为在测试过程中,试图借用对象,并将其从队列中删除。//回收测试完成后,它应该被返回到队列的头部。EVICTION_RETURN_TO_HEAD,//在队列中,正在被校验VALIDATION,//不在队列中,当前正在验证。该对象在验证时被借用,由于配置了testOnBorrow,//所以将其从队列中删除并预先分配。一旦验证完成,就应该分配它。VALIDATION_PREALLOCATED,//不在队列中,当前正在验证。在之前测试是否将该对象从队列中移除时,曾尝试借用该对象。//一旦验证完成,它应该被返回到队列的头部。VALIDATION_RETURN_TO_HEAD,//无效状态(如驱逐测试或验证),并将/已被销毁INVALID,//判定为无效,将会被设置为废弃ABANDONED,//正在使用完毕,返回池中RETURNING
}
流程理解
此段原文自 https://www.cnblogs.com/haixiang/p/14783955.html
对象存储
private PooledObject<T> create() throws Exception {.....final PooledObject<T> p;try {p = factory.makeObject();.....allObjects.put(new IdentityWrapper<>(p.getObject()), p);return p;}
我们查看allObjects,所有对象都存储于ConcurrentHashMap
,除了被杀掉的对象
/** All of the objects currently associated with this pool in any state. It* excludes objects that have been destroyed. The size of* {@link #allObjects} will always be less than or equal to {@link* #_maxActive}. Map keys are pooled objects, values are the PooledObject* wrappers used internally by the pool.*/
private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects =new ConcurrentHashMap<>();
对象取用逻辑
- 首先根据
AbandonedConfig
配置判断是否取用对象前执行清理操作 - 再从
idleObject
中尝试获取对象,获取不到就创建新的对象- 判断
blockWhenExhausted
是否设置为true
,是的话按照设置的borrowMaxWaitMillis
属性等待可用对象(这个配置的意思是当对象池的active
状态的对象数量已经达到最大值maxinum
时是否进行阻塞直到有空闲对象)
- 判断
- 有可用对象后调用工厂的
factory.activateObject
方法激活对象 - 当
getTestOnBorrow
设置为true
时,调用factory.validateObject(p)
对对象进行校验,通过校验后执行下一步 - 调用
updateStatsBorrow
方法,在对象被成功借出后更新一些统计项,例如返回对象池的对象个数等
//....
private final LinkedBlockingDeque<PooledObject<T>> idleObjects;
//....
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {assertOpen();final AbandonedConfig ac = this.abandonedConfig;if (ac != null && ac.getRemoveAbandonedOnBorrow() &&(getNumIdle() < 2) &&(getNumActive() > getMaxTotal() - 3) ) {removeAbandoned(ac);}PooledObject<T> p = null;// Get local copy of current config so it is consistent for entire// method executionfinal boolean blockWhenExhausted = getBlockWhenExhausted();boolean create;final long waitTime = System.currentTimeMillis();while (p == null) {create = false;p = idleObjects.pollFirst();if (p == null) {p = create();if (p != null) {create = true;}}if (blockWhenExhausted) {if (p == null) {if (borrowMaxWaitMillis < 0) {p = idleObjects.takeFirst();} else {p = idleObjects.pollFirst(borrowMaxWaitMillis,TimeUnit.MILLISECONDS);}}if (p == null) {throw new NoSuchElementException("Timeout waiting for idle object");}} else {if (p == null) {throw new NoSuchElementException("Pool exhausted");}}if (!p.allocate()) {p = null;}if (p != null) {try {factory.activateObject(p);} catch (final Exception e) {try {destroy(p, DestroyMode.NORMAL);} catch (final Exception e1) {// Ignore - activation failure is more important}p = null;if (create) {final NoSuchElementException nsee = new NoSuchElementException("Unable to activate object");nsee.initCause(e);throw nsee;}}if (p != null && getTestOnBorrow()) {boolean validate = false;Throwable validationThrowable = null;try {validate = factory.validateObject(p);} catch (final Throwable t) {PoolUtils.checkRethrow(t);validationThrowable = t;}if (!validate) {try {destroy(p, DestroyMode.NORMAL);destroyedByBorrowValidationCount.incrementAndGet();} catch (final Exception e) {// Ignore - validation failure is more important}p = null;if (create) {final NoSuchElementException nsee = new NoSuchElementException("Unable to validate object");nsee.initCause(validationThrowable);throw nsee;}}}}}updateStatsBorrow(p, System.currentTimeMillis() - waitTime);return p.getObject();}
对象的激活和钝化
在对象使用完被返回对象池时,如果校验失败直接销毁,如果校验通过需要先钝化对象passivateObject(PooledObject<T> p)
再存入空闲队列。至于激活对象的方法activateObject(PooledObject<T> p)
在上述取用对象时也会先激活再被取出。因此我们可以发现处于空闲和使用中的对象他们除了状态不一致,我们也可以通过激活和钝化的方式在他们之间增加新的差异
例如我们要做一个Elasticsearch连接池,每个对象就是一个带有ip和端口的连接实例,很显然访问es集群是多个不同的ip,所以每次访问的ip不一定相同,我们则可以在激活操作为对象赋值ip和端口,钝化操作中将ip和端口归为默认值或者空,这样流程更为标准
对象回收机制
EvictionTimer
为回收对象的定时器,当且仅当设置了timeBetweenEvictionRunsMillis
参数后才会开启定时清理任务
对象状态为evict,直接调用destroy进行回收。否则会进行进一步地处理,逻辑如下:
- 尝试激活对象,如果激活失败则认为对象已经不再存活,直接调用destroy进行销毁
- 在激活对象成功的情况下,会通过validateObject方法取校验对象状态,如果校验失败,则说明对象不可用,需要进行销毁
// org.apache.commons.pool2.impl.GenericObjectPool#evict
if (evict) {destroy(underTest);destroyedByEvictorCount.incrementAndGet();
} else {if (testWhileIdle) {boolean active = false;try {factory.activateObject(underTest);active = true;} catch (Exception e) {destroy(underTest);destroyedByEvictorCount.incrementAndGet();}if (active) {if (!factory.validateObject(underTest)) {destroy(underTest);destroyedByEvictorCount.incrementAndGet();} else {try {factory.passivateObject(underTest);} catch (Exception e) {destroy(underTest);destroyedByEvictorCount.incrementAndGet();}}}}if (!underTest.endEvictionTest(idleObjects)) {// TODO - May need to add code here once additional// states are used}
}
常用参数详解
参数 | 类型(默认值) | 描述 |
---|---|---|
lifo | boolean(true) | 当去获取对象池中的空闲实例时,是否需要遵循后进先出的原则 |
fairness | boolean(false) | 当对象池处于exhausted 状态,即可用实例为空时,大量线程在同时阻塞等待获取可用的实例,fairness 配置来控制是否启用公平锁算法,即先到先得。这一项的前提是blockWhenExhausted 配置为true |
maxTotal | int(8) | 对象池中最大使用数量 |
maxIdle | int(8) | 对象中空闲对象最大数量 |
minIdle | int(0) | 对象池中空闲对象最小数量 |
maxWaitMillis | long(-1) | 最大阻塞时间,当对象池处于exhausted状态,即可用实例为空时,大量线程在同时阻塞等待获取可用的实例,如果阻塞时间超过了maxWaitMillis将会抛出异常 |
testOnCreate | boolean(false) | 创建对象前,是否校验该新对象的有效性 |
testOnBorrow | boolean(false) | 取用对象前,是否检验对象的有效性 |
testOnReturn | boolean(false) | 归还对象前,是否检验对象的有效性 |
testWhileIdle | boolean(false) | 当回收器在扫描空闲对象时,是否校验对象的有效性 |
numTestsPerEvictionRun | int(3) | 根据该值x可以推导出一个数值n,标识回收过程需要检查多少个空闲对象。如果x>=0,那么n=x。如果x<0,那么n=(空闲对象数量/x的绝对值)向上取整,假设空闲对象一共有10个,该值配置为-3,那么就意味着这次回收需要检查4个空闲对象 |
timeBetweenEvictionRunsMillis | long(-1) | 回收器线程多久执行一次空闲对象回收 |
softMinEvictableIdleTimeMillis | long(-1) | 软回收时间阈值一个对象如果空闲时间超过了该值(毫秒),并且空闲对象的数量已经大于了minIdle时,就可以被回收器回收 |
minEvictableIdleTimeMillis | long(1000L * 60L * 30L) | 硬回收时间阈值一个对象如果空闲时间超过了该值,即使空闲对象的数量已经小于minIdle了,一样也会被回收器回收 |
假如在某个高负载的系统里,对象频繁被借出、被归还。此时推荐对象池配置为
testOnBorrow
、testOnReturn
都设置为false,提升性能timeBetweenEvictionRunsMillis
设置为60000,一分钟进行一次空闲对象的回收检查numTestsPerEvictionRun
设置为-1,检查所有空闲对象minEvictableIdleTimeMillis
设置为180000,空闲超过3分钟的可以被回收testWhileIdle
设置为true
,不管空闲时间是否超时,每个空闲对象都检查下有效性,无效的一样被回收
通过异步的回收器来尽可能的保证空闲对象的有效性,减少同步调用时有效性检查导致的响应延迟、以及有效性检查对底层带来的访问压力
总结
Apache Commons Pool2是一个用于对象池化的Java库,它提供了一种管理和重用对象实例的机制,旨在改善应用程序的性能和资源利用率。下面是对Apache Commons Pool2的总结:
- 对象池化:Apache Commons Pool2允许您创建和管理一个对象池,该对象池中保存着可重用的对象实例。通过将对象保存在池中而不是频繁地创建和销毁对象,可以减少系统开销,提高性能。
- 对象生命周期管理:Pool2提供了生命周期管理功能,允许您定义在对象从池中借出和归还时的初始化和清理操作。这确保了从池中借出的对象始终处于一种可用状态。
- 池化策略:Pool2支持多种池化策略,如通用对象池、软引用对象池和弱引用对象池。您可以根据应用程序的需求选择适合的策略。
- 线程安全:Pool2是线程安全的,它提供了同步机制来处理多个线程同时访问池的情况,确保对象的正确分配和归还。
- 配置灵活:Pool2允许您通过配置文件或编程方式来配置对象池的各种属性,如最大池大小、最小空闲对象数、池中对象的最大空闲时间等。这使得您可以根据应用程序的需求进行优化和调整。
- 连接池和资源池:Pool2不仅适用于对象池,还可用于连接池和其他资源的池化管理。这使得它在处理数据库连接、线程池等资源时非常有用。
总的来说,Apache Commons Pool2是一个功能强大且灵活的对象池化库,可帮助您管理和重用对象实例,提高应用程序的性能和资源利用率。无论是在Web应用程序、并发编程还是资源管理方面,Pool2都是一个可靠的选择
参考资料:
- Apache Commons Pool
- commons-pool2 池化技术探究
- 对象池
- apache common pool2原理与实战
相关文章:

Apache Commons Pool2 池化技术
对象池是一种设计模式,用于管理和重用对象,以提高性能和资源利用率。对象池的概念在许多应用程序中都有广泛应用,特别是在需要频繁创建和销毁对象的情况下,例如数据库连接、线程、HTTP连接等 对象池通过预先创建一组对象并将它们存…...

二叉树的最近公共祖先LCA
系列题目 236. 二叉树的最近公共祖先 1676. 二叉树的最近公共祖先IV 1644. 二叉树的最近公共祖先 II 235. 二叉搜索树的最近公共祖先 1650. 二叉树的最近公共祖先 III class LowestCommonAncestor:"""236. 二叉树的最近公共祖先题目强调p和q一定存在于二叉树中&…...

AWS SAA知识点整理(作成中)
共通 一些信息已经更新了,但参考题的答案还是旧的。 比如: S3的最大读写性能已经提高到 3,500 PUT/COPY/POST/DELETE or 5,500 GET/HEAD requests per second 并且不再要求使用random prefix 题目中有时候会让选择Not violation 不合适的一项ÿ…...
C++模板大全(持续更新,依不同网站整理而成)
C模板大全 基本模板快读快写快读快写火车头缺省源 基本算法暴力枚举模拟贪心二分三分尺取法分治前缀和差分递推递归倍增排序sort冒泡排序桶排序选择排序插入排序希尔排序归并排序快速排序堆排序计数排序基数排序 基础数据结构栈队列哈希链表单向链表双向链表 单调栈单调队列 高…...

《CTFshow-Web入门》10. Web 91~110
Web 入门 索引web91题解总结 web92题解总结 web93题解 web94题解 web95题解 web96题解 web97题解 web98题解 web99题解总结 web100题解 web101题解 web102题解 web103题解 web104题解 web105题解总结 web106题解 web107题解 web108题解 web109题解 web110题解 ctf - web入门 索…...

计组--总线
一、概念 总线是一组能为多个部件分时共享的公共信息传送线路。 共享是指总线上可以挂接多个部件,各个部件之间互相交换的信息都可以通过这组线路分时共享。 分时是指同一时刻只允许有一个部件向总线发送信息,如果系统中有多个部件,则它们…...
Git中的HEAD
Git中的HEAD HEAD^数字:表示当前提交的父提交,具体是第几个父提交通过数字指定,HEAD^1第一个父提交,该语法只 能用于合并(merge)的提交记录,因为一个通过合并产生的commit对象才有多个父提交。 HEAD~数字࿱…...

软件设计师_数据库系统_学习笔记
文章目录 3.1 数据库模式3.1.1 三级模式 两级映射3.1.2 数据库设计过程 3.2 ER模型3.3 关系代数与元组演算3.4 规范化理论3.5 并发控制3.6 数据库完整性约束3.7 分布式数据库3.8 数据仓库与数据挖掘 3.1 数据库模式 3.1.1 三级模式 两级映射 内模式直接与物理数据库相关联的 定…...

毛玻璃态计算器
效果展示 页面结构组成 从上述的效果可以看出,计算机的页面比较规整,适合grid布局。 CSS3 知识点 grid 布局 实现计算机布局 <div class"container"><form class"calculator" name"calc"><input type…...

常说的I2C协议是干啥的(电子硬件)
I2C(Inter-Integrated circuit)协议是电子传输信号中常用的一种协议。 它是一种两线式串行双向总线,用于连接微控制器和外部设备,也因为它所需的引脚数只需要两条(CLK和DATA),硬件实现简单&…...

C/C++进程超详细详解【中部分】(系统性学习day07)
目录 前言 一、守护进程 1.概念 2.守护进程创建的原理(如图清晰可见) 3.守护进程的实现(代码块) 二、dup和dup2 1,复制文件描述符 2.文件描述符重定向 三、系统日志 1,打开日志 2,向日…...
S型速度曲线轨迹规划(约束条件为速度和位移)
S型速度曲线规划的基础知识可以查看下面这篇博客: 带平滑功能的斜坡函数(多段曲线控温纯S型曲线SCL源代码+完整算法分析)_RXXW_Dor的博客-CSDN博客PLC运动控制基础系列之梯形速度曲线,可以参看下面这篇博客:PLC运动控制基础系列之梯形速度曲线_RXXW_Dor的博客-CSDN博客运…...

从零手搓一个【消息队列】实现数据的硬盘管理和内存管理(线程安全)
文章目录 一、硬盘管理1, 创建 DiskDataCenter 类2, init() 初始化3, 封装交换机4, 封装队列5, 关于绑定6, 关于消息 二、内存管理1, 数据结构的设计2, 创建 MemoryDataCenter 类3, 关于交换机4, 关于队列5, 关于绑定6, 关于消息7, 恢复数据 三、小结 创建 Spring Boot 项目, S…...

自动驾驶中的感知模型:实现安全与智能驾驶的关键
自动驾驶中的感知模型:实现安全与智能驾驶的关键 文章目录 引言感知模型的作用感知模型的技术安全与挑战结论 2023星火培训【专项营】Apollo开发者社区布道师倾力打造,包含PnC、新感知等的全新专项课程上线了。理论与实践相结合,全新的PnC培训…...

【CVPR 2023】DSVT: Dynamic Sparse Voxel Transformer with Rotated Sets
文章目录 开场白效果意图 重点VoxelNet: End-to-End Learning for Point Cloud Based 3D Object DetectionX-Axis DSVT LayerY-Axis DSVT Layer Dynamic Sparse Window AttentionDynamic set partitionRotated set attention for intra-window feature propagation.Hybrid wind…...

MySQL超入门(1)__迅速上手掌握MySQL
# 1.选择语句 # 注意事项:MySQL不区分大小写,SELECT * 代表选择全部 // 测试一 USE sql_store; -- 使用 sql_store库 SELECT * FROM customers -- 查询customers表 WHERE customer_id 1 OR customer_id 4 -- 条件判断为customer_id 1或customer_id …...

四、浏览器渲染过程,DOM,CSSDOM,渲染,布局,绘制详细介绍
知识点: 1、为什么不能先执行 js文件?? 我们不能先执行JS文件,必须等到CSSOM构建完成了才能执行JS文件,因为前面已经说过渲染树是需要DOM和CSSOM构建完成了以后才能构建,而且JS是可以操控CSS样式的&#…...

2021-06-10 51单片机设计一个蜂鸣器报警电路每秒
缘由求助一下谢谢啦51单片机_嵌入式-CSDN问答设计一个蜂鸣器报警电路,按下K1,蜂鸣器响一声,按下K2,蜂鸣器响三声,按下K3,蜂鸣器长鸣。要求响声和间隔的时间均为1秒,长鸣不限时,但是此时应设置一…...
D‘Agostino-Pearson正态检验|偏度skewness和峰度kurtosis
DAgostino-Pearson检验(也称为DAgostino和Pearson正态性检验)是一种用于检验数据是否符合正态分布的统计检验方法。它基于数据的样本统计量,主要包括偏度(skewness)和峰度(kurtosis),…...

基于树莓派CM4制作img系统镜像批量制作TF卡
文章目录 前言1. 环境与工具2. 制作镜像3. 烧录镜像4. 总结 前言 树莓派烧录完系统做定制化配置比较费时间。在面对大批量的树莓派要配置,那时间成本是非常巨大的。第一次配置完可以说是摸着石头过河,但是会弄了以后再配置,都是一些重复性操…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...

Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合
无论是python,或者java 的大型项目中,都会涉及到 自身平台微服务之间的相互调用,以及和第三发平台的 接口对接,那在python 中是怎么实现的呢? 在 Python Web 开发中,FastAPI 和 Django 是两个重要但定位不…...

Linux-进程间的通信
1、IPC: Inter Process Communication(进程间通信): 由于每个进程在操作系统中有独立的地址空间,它们不能像线程那样直接访问彼此的内存,所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...

HTTPS证书一年多少钱?
HTTPS证书作为保障网站数据传输安全的重要工具,成为众多网站运营者的必备选择。然而,面对市场上种类繁多的HTTPS证书,其一年费用究竟是多少,又受哪些因素影响呢? 首先,HTTPS证书通常在PinTrust这样的专业平…...