写点什么

对象池框架 commons pool2 原理与实践

  • 2025-01-23
    福建
  • 本文字数:2909 字

    阅读完需:约 10 分钟

当资源对象的创建/销毁比较耗时的场景下,可以通过"池化"技术,达到资源的复用,以此来减少系统的开销、增大系统吞吐量,比如数据库连接池、线程池、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; }
}
复制代码


2、定义对象工厂


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://www.cnblogs.com/makemylife/p/18686809

体验地址:http://www.jnpfsoft.com/?from=001YH

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
对象池框架 commons pool2 原理与实践_开发语言_不在线第一只蜗牛_InfoQ写作社区