当资源对象的创建/销毁比较耗时的场景下,可以通过"池化"技术,达到资源的复用,以此来减少系统的开销、增大系统吞吐量,比如数据库连接池、线程池、Redis 连接池等都是使用的该方式。

Apache Commons Pool 提供了通用对象池的实现,用于管理和复用对象,以提高系统的性能和资源利用率。

1 基础用法

1.1 添加依赖

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
</dependency>

1.2 定义对象工厂

PooledObjectFactory 是一个池化对象工厂接口,定义了生成对象、激活对象、钝化对象、销毁对象的方法,如下:

public interface PooledObjectFactory<T> {

  /**
* Creates an instance that can be served by the pool and wrap it in a
*/
PooledObject<T> makeObject() throws Exception; /**
* Destroys an instance no longer needed by the pool
*/
void destroyObject(PooledObject<T> p) throws Exception; /**
* Ensures that the instance is safe to be returned by the pool
*/
boolean validateObject(PooledObject<T> p); /**
* Reinitializes an instance to be returned by the pool
*/
void activateObject(PooledObject<T> p) throws Exception; /**
* Uninitializes an instance to be returned to the idle object pool
*/
void passivateObject(PooledObject<T> p) throws Exception;
}

以下是一个简单的示例:

  1. 定义需要池化的对象 MyObject
public class MyObject {

    private String uid = UUID.randomUUID().toString();

    private volatile boolean valid = true;

    public void initialize() {
System.out.println("初始化对象" + uid);
valid = true;
} public void destroy() {
System.out.println("销毁对象" + uid);
valid = false;
} public boolean isValid() {
return valid;
} public String getUid() {
return uid;
} }
  1. 定义对象工厂
public class MyObjectFactory implements PooledObjectFactory<MyObject> {

    @Override
public PooledObject<MyObject> makeObject() throws Exception {
// 创建一个新对象
MyObject object = new MyObject();
// 初始化对象
object.initialize();
return new DefaultPooledObject<>(object);
} @Override
public void destroyObject(PooledObject<MyObject> p) throws Exception {
// 销毁对象
p.getObject().destroy();
} @Override
public boolean validateObject(PooledObject<MyObject> p) {
return p.getObject().isValid();
} @Override
public void activateObject(PooledObject<MyObject> p) throws Exception {
} @Override
public void passivateObject(PooledObject<MyObject> p) throws Exception {
} }

1.3 配置对象池

创建 GenericObjectPool 对象,并设置相关参数,如最大对象数量、最小空闲对象数量等。

GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(20);
config.setMaxIdle(5);
config.setTestWhileIdle(true);
config.setMinEvictableIdleTimeMillis(60000L);
GenericObjectPool<MyObject> pool = new GenericObjectPool<>(new MyObjectFactory(), config);

1.4 借用和归还对象

MyObject myObject = null;
try {
myObject = pool.borrowObject();
System.out.println("get对象" + myObject.getUid() + " thread:" + Thread.*currentThread*().getName());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (myObject != null) {
pool.returnObject(myObject);
}
} catch (Exception e) {
e.printStackTrace();
}
}

2 Jedis 连接池

Jedis 是一个 Java 语言的 Redis 客户端库。它提供了一组易于使用的 API,可以用来连接和操作 Redis 数据库。

它的内部使用 Commons Pool 来管理 Redis 连接 ,我们使用 jedis 3.3.0 版本写一个简单的示例。

public class JedisMain {
public static void main(String[] args) throws Exception{
// 创建连接池配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(20);
// 创建连接池
JedisPool pool = new JedisPool(config, "localhost", 6379);
// 获取连接
Jedis jedis = pool.getResource();
jedis.set("hello" , "张勇");
// 使用连接
String value = jedis.get("hello");
System.out.println(value);
// 归还连接
jedis.close();
// 关闭连接池
// pool.close();
Thread.sleep(5000);
}
}

如下图,JedisFactory 实现了对象工厂,实现了创建对象销毁对象验证对象激活对象四个方法。

比如验证对象方法,逻辑是调用 Jedis 的 ping 方法,判断该连接是否存活。

3 原理解析

我们重点解析 GenericObjectPool 类的原理。

3.1 初始化

public GenericObjectPool(
final PooledObjectFactory<T> factory,
final GenericObjectPoolConfig<T> config) {
super(config, ONAME_BASE, config.getJmxNamePrefix());
if (factory == null) {
jmxUnregister(); // tidy up
throw new IllegalArgumentException("factory may not be null");
}
this.factory = factory;
idleObjects = new LinkedBlockingDeque<>(config.getFairness());
setConfig(config);
} private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
new ConcurrentHashMap<>();

初始化做三件事情:

  1. 初始化 JedisFactory 工厂对象。

  2. 对象容器 idleObjects , 类型是 LinkedBlockingDeque

    因此存储容器有两个,所有的对象 allObjects 和空闲对象 idleObjects (可以直接取出使用)。

  3. 配置对象池属性 。

3.2 创建对象

我们关注 GenericObjectPool 类的 borrowObject 方法。

逻辑其实很简单 :

  1. 从容器中获取第一个条目对象,若没有获取,则调用工厂对象的创建对象方法,并将该对象加入到全局对象 Map。

  2. 创建成功后,调用对象的激活方法,接着验证对象的可靠性,最后将对象返回。

3.3 归还连接

流程如下:

  1. 判断返还对象时是否校验,假如校验失败,则销毁该对象,将该对象从存储容器中删除 ;
  2. 调用工厂对象的激活对象方法 ;
  3. 若空闲对象 Map 元素大小达到最大值,则销毁该对象,将该对象从存储容器中删除 ;
  4. 正常将对象放回到空闲对象容器 idleObjects

参考资料:

https://github.com/redis/jedis/wiki/Getting-started

https://github.com/apache/commons-pool

对象池框架 commons pool2 原理与实践的更多相关文章

  1. Netty源码解析 -- 对象池Recycler实现原理

    由于在Java中创建一个实例的消耗不小,很多框架为了提高性能都使用对象池,Netty也不例外. 本文主要分析Netty对象池Recycler的实现原理. 源码分析基于Netty 4.1.52 缓存对象 ...

  2. Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类

    Java BasePooledObjectFactory 对象池化技术 通常一个对象创建.销毁非常耗时的时候,我们不会频繁的创建和销毁它,而是考虑复用.复用对象的一种做法就是对象池,将创建好的对象放入 ...

  3. Unity性能优化-对象池

    1.对象池Object Pool的原理: 有些GameObject是在游戏中需要频繁生成并销毁的(比如射击游戏中的子弹),以前的常规做法是:Instantiate不断生成预设件Prefab,然后采用碰 ...

  4. 对象池在 .NET (Core)中的应用[1]: 编程体验

    借助于有效的自动化垃圾回收机制,.NET让开发人员不在关心对象的生命周期,但实际上很多性能问题都来源于GC.并不说.NET的GC有什么问题,而是对象生命周期的跟踪和管理本身是需要成本的,不论交给应用还 ...

  5. 对象池在 .NET (Core)中的应用[2]: 设计篇

    <编程篇>已经涉及到了对象池模型的大部分核心接口和类型.对象池模型其实是很简单的,不过其中有一些为了提升性能而刻意为之的实现细节倒是值得我们关注.总的来说,对象池模型由三个核心对象构成,它 ...

  6. commons.pool2 对象池的使用

    commons.pool2 对象池的使用 ? 1 2 3 4 5 <dependency>     <groupId>org.apache.commons</groupI ...

  7. 基于Apache组件,分析对象池原理

    池塘里养:Object: 一.设计与原理 1.基础案例 首先看一个基于common-pool2对象池组件的应用案例,主要有工厂类.对象池.对象三个核心角色,以及池化对象的使用流程: import or ...

  8. Apache common pool2 对象池

    对象池的容器:包含一个指定数量的对象.从池中取出一个对象时,它就不存在池中,直到它被放回.在池中的对象有生命周期:创建,验证,销毁,对象池有助于更好地管理可用资源,防止JVM内部大量临时小对象,频繁触 ...

  9. Java--对象池化技术 org.apache.commons.pool2.ObjectPool

    org.apache.commons.pool2.ObjectPool提供了对象池,开发的小伙伴们可以直接使用来构建一个对象池 使用该对象池具有两个简单的步骤: 1.创建对象工厂,org.apache ...

  10. 对象池化技术 org.apache.commons.pool

    恰当地使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率.Jakarta Commons Pool组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现,可以 ...

随机推荐

  1. 2023NOIP A层联测26 T3 tour

    2023NOIP A层联测26 T3 tour 有意思的树上主席树. 思路 首先考虑一个点 \(p\) 能计入答案的情况,就是 \(dis(x,p)-a_p \ge a_p\). 我们把 \(x \t ...

  2. Flink 实战之 Real-Time DateHistogram

    系列文章 Flink 实战之 Real-Time DateHistogram Flink 实战之从 Kafka 到 ES DateHistogram 用于根据日期或时间数据进行分桶聚合统计.它允许你将 ...

  3. 【一步步开发AI运动小程序】九、姿态辅助调试桌面工具的使用

    随着人工智能技术的不断发展,阿里体育等IT大厂,推出的"乐动力"."天天跳绳"AI运动APP,让云上运动会.线上运动会.健身打卡.AI体育指导等概念空前火热.那 ...

  4. ubuntu安装fish

    换新电脑后需要安装fish命令行工具,发现总是apt install不成功,后来挂了代理才成功. 然后我想让这个fish的命令能自动导入我以前写的alias命令(点击这里),可是发现网上人家都说fis ...

  5. 使用Docker快速部署一个Net项目

    前言 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化. 优点 Web 应用的自动化打包和发布. 自动化测试和 ...

  6. 张高兴的 Raspberry Pi AI 开发指南:(三)将自定义模型编译为 Hailo NPU 的 .hef 模型

    目录 Python 环境配置 转换 量化 编译 参考 在上一篇博客中,探讨了如何使用 Python 和 hailo_model_zoo 中预编译的模型来实现目标检测.本篇博客将深入介绍如何将用户自定义 ...

  7. 解决Your project does not reference问题

    错误现象 vs编译时,报错: Your project does not reference ".NETFramework,Version=v##" framework. Add ...

  8. RabbitMQ 快速入门

    RabbitMQ 快速入门 官网:https://www.rabbitmq.com/ 入门教程:https://www.rabbitmq.com/tutorials 最新版本:4.0.2 版本参考:J ...

  9. 使用Python实现两组数据纵向排序

    一.引言 在数据分析和处理过程中,排序是一项非常常见的操作.排序操作能够让我们更清晰地理解数据,从而进行进一步的分析和处理.在Python中,排序操作通常可以通过内置函数或第三方库来实现.本文将详细讲 ...

  10. 腾讯云 TStor 私有云存储获统信+海光/兆芯官方认证

    腾讯云 TStor 是一款分布式存储产品,致力于解决私有云.混合云下的各类存储需求.产品紧跟国内信创生态,持续加强自主可控能力.目前,TStor 已经支持国内主流国产操作系统和硬件,如中标麒麟操作系统 ...