对象池架构设计
Hutool的对象池实现基于分区设计,参考了fast-object-pool和commons-pool的优秀设计,提供了高效、线程安全的对象管理机制。
# 1. 核心架构
# 1.1 整体设计思路
Hutool对象池采用分区设计模式,将对象池分为多个独立的分区,每个分区拥有自己的对象队列,通过线程ID将线程分配到固定分区,从而减少线程竞争,提高并发性能。
# 1.2 核心组件关系
┌─────────────────────────────────────────────────────────────────────────┐
│ ObjectPool<T> │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ PartitionObjectPool<T> │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ PoolPartition<T>[] │ │ │
│ │ │ ┌───────────────────────────────────────────────────────┐ │ │ │
│ │ │ │ Poolable<T>[] │ │ │ │
│ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │
│ │ │ │ │ SimplePoolable<T> │ │ │ │ │
│ │ │ │ │ ┌───────────────────────────────────────────┐ │ │ │ │ │
│ │ │ │ │ │ T (原始对象) │ │ │ │ │ │
│ │ │ │ │ └───────────────────────────────────────────┘ │ │ │ │ │
│ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │
│ │ │ └───────────────────────────────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
▲ ▲
│ │
│ │
┌───────────┴───────────┐ ┌─────────────┴─────────────┐
│ ObjectFactory<T> │ │ PoolConfig │
└───────────────────────┘ └───────────────────────────┘
▲
│
┌───────────┴───────────┐
│ PartitionPoolConfig │
└───────────────────────┘
# 2. 核心接口
# 2.1 ObjectPool
对象池的核心接口,定义了对象的借出、归还和销毁等基本操作:
borrowObject():借出对象returnObject(T obj):归还对象free(T obj):销毁对象getTotal():获取池中的总对象数getIdleCount():获取池中的空闲对象数getActiveCount():获取正在使用的对象数
# 2.2 ObjectFactory
对象工厂接口,用于自定义对象的创建、验证和销毁逻辑:
create():创建新对象validate(T t):验证对象是否可用destroy(T t):销毁对象
# 2.3 Poolable
池化对象接口,包装原始对象并提供对象的空闲时间等信息:
getRaw():获取原始对象getIdle():获取对象空闲时长getLastReturn():获取最后归还时间setLastReturn(long lastReturn):设置最后归还时间
# 3. 核心类
# 3.1 PoolConfig
对象池的基本配置类,提供以下配置项:
| 配置项 | 描述 | 默认值 |
|---|---|---|
| minSize | 最小(初始)池大小 | 5 |
| maxSize | 最大池大小 | 20 |
| maxWait | 借出对象时的最长等待时间 | 5000ms |
| maxIdle | 对象在池中的最长空闲时间 | 0(不限制) |
# 3.2 SimplePoolable
简单的池化对象实现,包装原始对象并记录最后归还时间。
# 3.3 PartitionObjectPool
分区对象池的核心实现,将对象池分为多个分区,每个分区有自己的对象队列:
- 线程安全:分区创建后不再变更
- 并发优化:根据线程ID分配固定分区,减少竞争
- 可扩展:支持自定义阻塞队列和分区分配策略
# 3.4 PoolPartition
分区实现,每个分区包含以下功能:
- 对象的创建、借出、归还和销毁
- 对象可用性验证
- 空闲对象管理
# 3.5 PartitionPoolConfig
分区池配置,扩展了PoolConfig,增加了分区大小配置:
| 配置项 | 描述 | 默认值 |
|---|---|---|
| partitionSize | 分区数量 | 2 |
# 4. 工作原理
# 4.1 对象借出流程
- 检查对象池是否已关闭
- 根据线程ID获取分配的分区
- 从分区的阻塞队列中尝试获取对象
- 如果队列中有对象,验证对象可用性:
- 可用则返回原始对象
- 不可用则销毁并继续尝试获取
- 如果队列为空,且当前对象数小于最大池大小:
- 创建新对象
- 包装为池化对象
- 返回原始对象
- 如果队列已满且无可用对象,等待最大等待时间:
- 超时则抛出异常
- 期间有对象归还则获取并返回
# 4.2 对象归还流程
- 检查对象池是否已关闭
- 根据线程ID获取分配的分区
- 验证对象可用性:
- 不可用则销毁
- 可用则包装为池化对象
- 将池化对象放入分区的阻塞队列
- 如果队列已满,销毁对象
# 4.3 对象销毁流程
- 调用ObjectFactory的destroy方法销毁原始对象
- 从池中移除该对象
# 5. 架构优势
# 5.1 分区设计
- 减少线程竞争:每个分区有自己的对象队列,线程固定分配到一个分区
- 提高并发性能:分区之间相互独立,避免了全局锁竞争
- 更好的扩展性:可以根据系统负载调整分区数量
# 5.2 灵活的对象管理
- 自定义对象工厂:支持自定义对象的创建、验证和销毁逻辑
- 可配置的池大小:根据业务需求调整最小和最大池大小
- 智能的对象回收:自动销毁空闲时间过长的对象
# 5.3 线程安全
- 分区独立:每个分区的对象队列是线程安全的
- 原子操作:对象的借出、归还和销毁都是原子操作
- 安全关闭:支持安全关闭对象池,释放所有资源
# 6. 使用场景
# 6.1 适合使用对象池的场景
- 对象创建成本高:如数据库连接、网络连接等
- 对象复用率高:需要频繁创建和销毁的对象
- 并发访问量大:多个线程同时访问相同类型的对象
# 6.2 不适合使用对象池的场景
- 对象创建成本低:如简单的POJO对象
- 对象状态复杂:难以验证对象的可用性
- 单线程环境:没有并发竞争的场景
# 7. 设计模式
Hutool对象池实现中使用了多种设计模式:
# 7.1 工厂模式
- ObjectFactory:负责对象的创建、验证和销毁
- 优势:解耦对象创建和使用,方便扩展不同类型的对象池
# 7.2 单例模式
- PoolConfig:提供静态工厂方法创建配置对象
- 优势:简化配置对象的创建过程
# 7.3 分区模式
- PartitionObjectPool:将对象池分为多个独立的分区
- 优势:减少线程竞争,提高并发性能
# 7.4 装饰器模式
- Poolable:包装原始对象,添加池化特性
- 优势:不修改原始对象,添加额外的池化功能
# 8. 与其他对象池的比较
| 特性 | Hutool对象池 | Commons Pool2 | fast-object-pool |
|---|---|---|---|
| 分区设计 | ✅ | ❌ | ✅ |
| 线程安全 | ✅ | ✅ | ✅ |
| 自定义对象工厂 | ✅ | ✅ | ✅ |
| 可配置池大小 | ✅ | ✅ | ✅ |
| 空闲对象回收 | ✅ | ✅ | ✅ |
| 最大等待时间 | ✅ | ✅ | ✅ |
| 轻量级 | ✅ | ❌ | ✅ |
| 依赖简单 | ✅ | ❌ | ✅ |
# 9. 扩展性设计
Hutool对象池提供了良好的扩展性:
# 9.1 自定义阻塞队列
通过重写createBlockingQueue方法,可以使用不同类型的阻塞队列:
protected BlockingQueue<Poolable<T>> createBlockingQueue(final PartitionPoolConfig poolConfig) {
// 使用LinkedBlockingQueue替代默认的ArrayBlockingQueue
return new LinkedBlockingQueue<>();
}
# 9.2 自定义分区分配策略
通过重写getPartitionIndex方法,可以自定义线程到分区的分配策略:
protected int getPartitionIndex(final PartitionPoolConfig poolConfig) {
// 使用线程名称哈希值分配分区
return Math.abs(Thread.currentThread().getName().hashCode()) % poolConfig.getPartitionSize();
}
# 9.3 自定义对象工厂
实现ObjectFactory接口,可以自定义对象的创建、验证和销毁逻辑:
public class CustomObjectFactory implements ObjectFactory<CustomObject> {
@Override
public CustomObject create() {
return new CustomObject();
}
@Override
public boolean validate(CustomObject obj) {
return obj.isValid();
}
@Override
public void destroy(CustomObject obj) {
obj.close();
}
}
# 10. 总结
Hutool的对象池实现采用了分区设计,提供了高效、线程安全的对象管理机制。其核心优势包括:
- 分区设计:减少线程竞争,提高并发性能
- 灵活配置:支持多种配置项,适应不同场景
- 易于扩展:支持自定义对象工厂、阻塞队列和分区分配策略
- 轻量级:依赖简单,易于集成
- 线程安全:分区独立,原子操作
通过合理使用对象池,可以有效降低对象创建成本,提高系统性能和资源利用率。