自学内容网 自学内容网

池化技术、Commons Pool介绍

概述

池化技术,一种通过重复利用对象实例而非频繁创建和销毁的技术。

常见的可池化对象:

  • 数据库连接(Connection):数据库连接创建和销毁代价高,连接池广泛用于管理JDBC连接;
  • 线程(Thread):线程的创建和销毁开销大,线程池是多线程编程中常用的技术;
  • 网络连接(Socket/HTTPClient):复用HTTP连接以提高网络通信效率;
  • 对象实例(Object):对于短生命周期且频繁使用的对象,可通过对象池管理其生命周期;
  • 内存块或缓冲区(Buffer):I/O操作中经常使用缓冲池(如Netty的ByteBuf池)来减少内存分配和垃圾回收的开销;
  • 会话或上下文对象(Session/Context):在Web应用或分布式系统中,Session对象可通过池化优化访问性能。

具体的池化技术:

  • 数据库连接池:如HikariCP、Druid、C3P0;管理JDBC连接生命周期,包括创建、复用和回收。
  • 线程池:如Java中的ExecutorService和ThreadPoolExecutor。控制线程的并发量和资源使用,避免频繁创建线程。
  • HTTP连接池:如OkHttp、Apache HttpClient。复用HTTP连接,减少TCP连接开销。
  • 对象池:如Apache Commons Pool、HikariCP的对象池管理。用于频繁创建和销毁的轻量级对象实例。
  • 缓冲区池:如Netty的内存池(ByteBufAllocator),池化I/O缓冲区以减少垃圾回收的负担。
  • GPU内存池:如CUDA的内存池,在深度学习或图像处理场景中复用显存资源。
  • 分布式对象池:如Redisson对Redis资源的池化封装,管理分布式系统中的共享资源。

当遇到以下场景,可考虑使用池化技术来提高系统性能:

  • 对象的创建或销毁很频繁,需要使用较多的系统资源,如数据库连接、线程、HTTP请求;
  • 生命周期较短且重复使用的对象,如临时计算对象、缓冲区;
  • 需要高并发和低延迟,如Web服务、实时数据流处理;
  • 资源昂贵的操作,如GPU显存管理、大型文件处理。

优势

  • 减少资源开销:通过复用降低对象的创建和销毁成本;
  • 提高性能:更快的响应时间和更少的垃圾回收;
  • 降低复杂度:池化工具通常内置优化策略(如负载均衡、超时管理)。

注意事项

  • 资源竞争:池的大小配置过小可能导致资源争抢,过大则浪费资源;
  • 池中对象管理:需要避免对象泄漏或资源未正确释放;
  • 过期资源处理:池化资源可能因为网络或连接原因失效,需实现健康检查和自动回收。

线程池

参考面试必备之线程池(Java),以及ForkJoinPool,以及MySQL线程池

连接池

参考JDBC与数据库连接池

缓冲池

MySQL提供缓冲池能力,待学习。

对象池

提到对象池,不得不说ApacheGitHub的Commons Pool。

官方文档

核心组件(API):

  • 对象池(ObjectPool):实现对对象存取和状态管理的池实现,如:线程池、数据库连接池;
  • 池化对象(PooledObject):池化对象,放在ObjectPool里的包装类。添加一些附加信息,如状态,创建时间,激活时间,关闭时间等;
  • 池化对象工厂(PooledObjectFactory):工厂类,负责具体对象的创建、初始化、销毁和状态验证。

关系图
在这里插入图片描述

实战

引入如下依赖:

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

实现一个简单的数据库连接池:

@AllArgsConstructor
public class DatabaseConnectionFactory extends BasePooledObjectFactory<Connection> {
    private String connectionString;
    private String username;
    private String password;

    // 创建新的数据库连接
    @Override
    public Connection create() {
        try {
            return DriverManager.getConnection(connectionString, username, password);
        } catch (SQLException e) {
            throw new RuntimeException("创建数据库连接失败", e);
        }
    }

    // 销毁数据库连接
    @Override
    public void destroyObject(PooledObject<Connection> p) throws Exception {
        p.getObject().close();
    }

    // 验证数据库连接是否有效
    @Override
    public boolean validateObject(PooledObject<Connection> p) {
        try {
            return p.getObject().isValid(1); // 设置一个非常短的超时,仅用于检查连接是否仍然可用
        } catch (SQLException e) {
            return false;
        }
    }

    // 激活对象,可选,No-op
    @Override
    public void activateObject(PooledObject<Connection> p) throws Exception {
        // 可在这里进行一些连接重新激活的操作,例如设置自动提交、隔离级别等
    }

    // 钝化对象,可选,No-op
    @Override
    public void passivateObject(PooledObject<Connection> p) throws Exception {
        // 可在对象返回到池之前执行一些清理或重置操作
    }
}

配置和创建ObjectPool:

public class DatabaseConnectionPool {
    private static GenericObjectPool<Connection> pool;

    static {
        // 配置连接池的参数
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxTotal(10); // 设置连接池的最大连接数
        config.setMaxIdle(5); // 设置连接池的最大空闲连接数
        config.setMinIdle(2); // 设置连接池的最小空闲连接数

        DatabaseConnectionFactory factory = new DatabaseConnectionFactory("jdbc:mysql://localhost:3306/mydatabase", "user", "password");
        // 初始化连接池
        pool = new GenericObjectPool<>(factory, config);
    }

    // 获取数据库连接
    public static Connection getConnection() throws Exception {
        return pool.borrowObject();
    }

    // 归还数据库连接到池
    public static void releaseConnection(Connection conn) {
        if (conn != null) {
            pool.returnObject(conn);
        }
    }

    // 关闭连接池(通常在应用程序关闭时调用)
    public static void close() {
        if (pool != null) {
            pool.close();
        }
    }
}

最后是测试类:

public static void main(String[] args) {
    try (Connection conn = DatabaseConnectionPool.getConnection()) {
        // 使用连接执行数据库操作        
        // 连接会在try-with-resources块结束时自动归还到池中
    } catch (Exception e) {
        // log
    }
    // 注意:在应用程序结束时,应该调用DatabaseConnectionPool.close()来关闭连接池
}

原理

大致如下图
在这里插入图片描述
注:

  • minEvictableldleTimeMillis是早期版本的拼写,最新版2.12.0对应的参数是minEvictableIdleDuration;
  • softMinEvictableldleTimeMillis对应的参数是softMinEvictableIdleDuration;
  • timeBetweenEvictionRunsMillis对应的参数是durationBetweenEvictionRuns。

4个test参数:

  • testOnCreate:指定在创建时,是否对池化对象进行有效性检测;以下类似
  • testOnBorrow:获取
  • testOnReturn:归还
  • testWhileIdle:空闲检测

生产环境,建议只将testWhileIdle设置为true,其他几个参数设置为false,并通过调整空闲检测时间间隔(timeBetweenEvictionRunsMillis,durationBetweenEvictionRuns)来保证资源的可用性和效率。

源码

ObjectPool:

public interface ObjectPool<T> extends Closeable {
// 添加对象
void addObject() throws Exception;
// 添加多个对象
default void addObjects(final int count) throws Exception {
for (int i = 0; i < count; i++) {
addObject();
}
}
// 从池中获取对象
T borrowObject() throws Exception;
// 清除池,池可用
void clear() throws Exception;
// 关闭池,池不可用
@Override
void close();
// 获取活跃对象个数
int getNumActive();
// 获取空闲对象个数
int getNumIdle();
// 废弃对象
void invalidateObject(T obj) throws Exception;
// 使用指定的废弃模式废弃对象
default void invalidateObject(final T obj, final DestroyMode destroyMode) throws Exception {
invalidateObject(obj);
}
// 将对象放回池中
void returnObject(T obj) throws Exception;
}

枚举类DestroyMode源码如下:

public enum DestroyMode {
// 常规废弃
NORMAL,
// Destroy abandoned object
ABANDONED
}

PooledObject:

public interface PooledObject<T> extends Comparable<PooledObject<T>> {
static boolean isNull(final PooledObject<?> pooledObject) {
return pooledObject == null || pooledObject.getObject() == null;
}

boolean allocate();

boolean deallocate();

boolean endEvictionTest(Deque<PooledObject<T>> idleQueue);

default Duration getActiveDuration() {
// Take copies to avoid threading issues
final Instant lastReturnInstant = getLastReturnInstant();
final Instant lastBorrowInstant = getLastBorrowInstant();
// @formatter:off
return lastReturnInstant.isAfter(lastBorrowInstant) ?
Duration.between(lastBorrowInstant, lastReturnInstant) :
Duration.between(lastBorrowInstant, Instant.now());
// @formatter:on
}
@Deprecated
default Duration getActiveTime() {
return getActiveDuration();
}
@Deprecated
long getActiveTimeMillis();

default long getBorrowedCount() {
return -1;
}

default Instant getCreateInstant() {
return Instant.ofEpochMilli(getCreateTime());
}
@Deprecated
long getCreateTime();

default Duration getFullDuration() {
return Duration.between(getCreateInstant(), Instant.now());
}

default Duration getIdleDuration() {
return Duration.ofMillis(getIdleTimeMillis());
}
@Deprecated
default Duration getIdleTime() {
return Duration.ofMillis(getIdleTimeMillis());
}
@Deprecated
long getIdleTimeMillis();

default Instant getLastBorrowInstant() {
return Instant.ofEpochMilli(getLastBorrowTime());
}
@Deprecated
long getLastBorrowTime();

default Instant getLastReturnInstant() {
return Instant.ofEpochMilli(getLastReturnTime());
}
@Deprecated
long getLastReturnTime();

default Instant getLastUsedInstant() {
return Instant.ofEpochMilli(getLastUsedTime());
}
@Deprecated
long getLastUsedTime();

T getObject();

PooledObjectState getState();

void invalidate();

void markAbandoned();

void markReturning();

void printStackTrace(PrintWriter writer);

void setLogAbandoned(boolean logAbandoned);

default void setRequireFullStackTrace(final boolean requireFullStackTrace) {
// noop
}

boolean startEvictionTest();
void use();
@Override
int compareTo(PooledObject<T> other);
@Override
boolean equals(Object obj);
@Override
int hashCode();
String toString();
}

PooledObjectFactory,泛型接口:

public interface PooledObjectFactory<T> {
void activateObject(PooledObject<T> p) throws Exception;
void destroyObject(PooledObject<T> p) throws Exception;
default void destroyObject(final PooledObject<T> p, final DestroyMode destroyMode) throws Exception {
    destroyObject(p);
}
PooledObject<T> makeObject() throws Exception;
void passivateObject(PooledObject<T> p) throws Exception;
boolean validateObject(PooledObject<T> p);
}

解读:

  • makeObject:创建一个新对象;当对象池中的对象个数不足(个数配置,参考下面的Config)时,将会使用此方法来产生一个新的对象,并交付给对象池管理;
  • destroyObject:销毁对象,如果检测到对象池中某个对象idle的时间超时,或操作者向对象池归还对象时检测到对象已经无效,则触发对象销毁。调用此方法时,对象的生命周期必须结束。如果object是线程,则线程必须退出;如果是Socket操作,则Socket必须关闭;如果是文件流操作,则此时数据flush且正常关闭。
  • validateObject:检测对象是否有效;Pool中不能保存无效的对象,因此后台检测线程会周期性的检测Pool中对象的有效性,如果对象无效则会导致此对象从Pool中移除,并destroy;此外在调用者从Pool获取一个对象时,也会检测对象的有效性,确保不会将无效的对象输出给调用者;当调用者使用完毕将对象归还到Pool时,仍然会检测对象的有效性。有效性,就是此对象的状态是否符合预期,调用者是否可直接使用;如果对象是Socket,有效性则意味着Socket的通道是否畅通/阻塞是否超时等。
  • activateObject:激活对象,当Pool中决定移除一个对象交付给调用者时额外的激活操作,比如可在activateObject方法中重置参数列表让调用者使用时感觉像一个新创建的对象一样。如果object是线程,可在激活操作中重置线程中断标记,或让线程从阻塞中唤醒等;如果是Socket,则可在激活操作中刷新通道,或对Socket进行连接重建(假如Socket意外关闭)等。
  • passivateObject:钝化对象,当调用者归还对象时,Pool将会钝化对象。如果object是Socket,则清除buffer,将Socket阻塞;如果是线程,可在钝化操作中将线程sleep或将线程中的某个对象wait。activateObject和passivateObject两个方法需要一一对应,避免死锁或对象状态的混乱。

BaseObjectPoolConfig提供的配置:

  • lifo:连接池放池化对象方式,默认为true
  • true:放在空闲队列最前面
  • false:放在空闲队列最后面
  • fairness:等待线程拿空闲连接的方式,默认为false
  • true:相当于等待线程是在先进先出去拿空闲连接
  • maxWaitMillis:当连接池资源耗尽时,调用者最大阻塞的时间,超时将抛出异常。单位为毫秒数;默认为-1,表示永不超时。已废弃,请使用maxWaitDuration
  • minEvictableIdleTimeMillis:连接空闲的最小时间,达到此值后空闲连接将可能会被移除。负值(-1)表示不移除;默认值1000L 60L 30L。已废弃,请使用minEvictableIdleDuration
  • softMinEvictableIdleTimeMillis:连接空闲的最小时间,达到此值后空闲连接将会被移除,且保留minIdle个空闲连接数。已废弃,请使用softMinEvictableIdleDuration
  • numTestsPerEvictionRun:对于空闲连接检测线程而言,每次检测的连接资源的个数。默认值3
  • evictionPolicyClassName:默认值org.apache.commons.pool2.impl.DefaultEvictionPolicy
  • testOnCreate:默认值false
  • testOnBorrow:向调用者输出连接资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。默认为false。建议保持默认值
  • testOnReturn:默认值false
  • testWhileIdle:向调用者输出连接对象时,是否检测它的空闲超时;默认为false。如果连接空闲超时,将会被移除;建议保持默认值。默认值false
  • timeBetweenEvictionRunsMillis:空闲连接检测线程,检测周期,单位是毫秒数。如果为负值,表示不运行检测线程。默认值为-1。已废弃,请使用durationBetweenEvictionRuns
  • durationBetweenEvictionRuns:
  • blockWhenExhausted:默认值true
  • jmxEnabled:默认值true
  • jmxNamePrefix:默认值pool
  • jmxNameBase:默认值null
  • maxTotal:连接池中最大连接数

参考


原文地址:https://blog.csdn.net/lonelymanontheway/article/details/144024510

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!