池化技术、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)!