聊聊对象池框架 commons pool2

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

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

以下是一个简单的示例:

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

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

    privatevolatileboolean 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;
    }

}
  • 定义对象工厂
public class MyObjectFactory implements PooledObjectFactory<MyObject> {

    @Override
    public PooledObject<MyObject> makeObject() throws Exception {
        // 创建一个新对象
        MyObject object = new MyObject();
        // 初始化对象
        object.initialize();
        returnnew 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
          thrownew IllegalArgumentException("factory may not be null");
     }
     this.factory = factory;
     idleObjects = new LinkedBlockingDeque<>(config.getFairness());
     setConfig(config);
 }

privatefinal Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
        new ConcurrentHashMap<>();

初始化做三件事情:

  • 初始化 JedisFactory 工厂对象。
  • 对象容器 idleObjects , 类型是 LinkedBlockingDeque 。因此存储容器有两个,所有的对象 allObjects 和空闲对象 idleObjects (可以直接取出使用)。
  • 配置对象池属性 。

3.2 创建对象

我们关注 GenericObjectPool 类的 borrowObject 方法。

图片图片

逻辑其实很简单 :

  • 从容器中获取第一个条目对象,若没有获取,则调用工厂对象的创建对象方法,并将该对象加入到全局对象 Map。
  • 创建成功后,调用对象的激活方法,接着验证对象的可靠性,最后将对象返回。

3.3 归还连接

图片图片

流程如下:

  • 判断返还对象时是否校验,假如校验失败,则销毁该对象,将该对象从存储容器中删除 ;
  • 调用工厂对象的激活对象方法 ;
  • 若空闲对象 Map 元素大小达到最大值,则销毁该对象,将该对象从存储容器中删除 ;
  • 正常将对象放回到空闲对象容器 idleObjects 。
责任编辑:武晓燕 来源: 勇哥Java实战
相关推荐

2017-04-19 11:22:11

demoPool2Java

2021-11-29 09:38:12

设计模式对象池模式Object Pool

2017-02-27 16:43:34

Golang多线程编程

2019-06-24 05:05:40

缓冲池查询数据InnoDB

2022-03-22 15:05:15

MySQL缓冲池

2021-10-17 22:40:51

JavaScript开发 框架

2022-02-09 11:02:16

JavaScript前端框架

2021-04-20 08:01:35

5W2H方法框架

2024-12-03 10:21:56

2023-10-07 15:56:49

三链表缓存页flush链表

2024-06-11 09:22:51

2024-05-11 11:18:21

Kafka监控框架

2021-02-01 08:28:24

Linux线程池Linux系统

2022-08-29 09:06:43

hippo4j动态线程池

2021-10-27 11:29:49

Linux框架内核

2010-03-31 10:07:09

Oracle shar

2022-12-12 08:42:06

Java对象栈内存

2018-03-27 10:06:26

对象存储演进

2023-07-11 08:34:25

参数流程类型

2020-06-11 11:36:49

线程池Java场景
点赞
收藏

51CTO技术栈公众号