commons-pool2源码走读(四) 对象池实现GenericObjectPool<T>
GenericObjectPool <T> 是一个可配置的ObjectPool实现。
当与适当的PooledObjectFactory组合使用时,GenericObjectPool为任意对象提供健壮的池功能。
您可以选择性的配置池来检查和可能回收池中的空闲对象,并确保有最少数量的空闲对象可用。这是由一个“空闲对象回收”线程(即BaseGenericObjectPool <T> 的Evictor)执行的,线程是异步运行的。在配置这个可选特性时,应该谨慎使用。驱逐运行与客户端线程争用池中的对象,因此如果它们运行得太频繁,可能会导致性能问题。
还可以配置池来检测和删除被泄漏的对象,比如一个从池中借出的对象,在超过removeAbandonedTimeout超时之前既不使用也不返回。移除泄漏的连接,可能发生在对象被借用时对象池已接近饱和,也可能是被回收线程检查出,或者两者都执行时。如果池对象实现了TrackedUse接口,那么其最后一次使用时间使取决于getLastUsed方法;否则,是由对象从池中借出的时间决定。
实现注意:为了防止可能的死锁,已经采取了谨慎措施,以确保在同步块中不会发生对工厂方法的调用。这个类线程安全。
1、接口继承、实现关系
GenericObjectPool <T> 实现了ObjectPool<T> 具备对象池的功能,同时 继承了BaseGenericObjectPool<T> 的对于对象状态管理和回收等功能。
2、构造函数
构造函数通过GenericObjectPoolConfig 和PooledObjectFactory来进行参数的初始化和对象工厂类的引入。
public GenericObjectPool(final PooledObjectFactory<T> factory,
final GenericObjectPoolConfig config) {
//父类BaseGenericObjectPool构造方法
super(config, ONAME_BASE, config.getJmxNamePrefix());
if (factory == null) {
jmxUnregister(); // tidy up
throw new IllegalArgumentException("factory may not be null");
}
this.factory = factory;
//空闲对象队列,此队列非JDK而是自行实现的一个队列
idleObjects = new LinkedBlockingDeque<>(config.getFairness());
//覆盖BaseGenericObjectPool里面的配置参数
setConfig(config);
//初始化回收线程
startEvictor(getTimeBetweenEvictionRunsMillis());
}
3、相关属性
// --- 可配置的属性 -------------------------------------------------
//最大空闲数量
private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;
//最小空闲数量
private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;
//对象工厂
private final PooledObjectFactory<T> factory;
// --- 内部属性 -------------------------------------------------
//池中所有的对象,只能是<=maxActive
private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
new ConcurrentHashMap<>();
//已创建对象总数(不包含已销毁的)
private final AtomicLong createCount = new AtomicLong(0);
//调用创建方法总线程数
private long makeObjectCount = 0;
//makeObjectCount 增长时并发锁
private final Object makeObjectCountLock = new Object();
//空闲对象队列
private final LinkedBlockingDeque<PooledObject<T>> idleObjects;
// JMX specific attributes
private static final String ONAME_BASE =
"org.apache.commons.pool2:type=GenericObjectPool,name=";
//泄漏对象回收配置参数
private volatile AbandonedConfig abandonedConfig = null;
4、 对象池方法实现
- 借用对象
整个流程为,检查池是否关闭 –> 是否回收泄漏对象 –> 是否阻塞创建对象 –> 创建对象 –> 分配对象 –> 激活对象 –> 校验对象 –> 更改借用信息 –> 返回对象
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
//判断对象池是否关闭:BaseGenericObjectPool.closed==true
assertOpen();
//如果回收泄漏的参数配置不为空,并且removeAbandonedOnBorrow参数配置为true
//并且Idle数量<2,Active数量>总数Total-3
//在借用时进行回收泄漏连接(会影响性能)
final AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
(getNumIdle() < 2) &&
(getNumActive() > getMaxTotal() - 3) ) {
//回收泄漏对象
removeAbandoned(ac);
}
PooledObject<T> p = null;
//copy blockWhenExhausted 防止其它线程更改getBlockWhenExhausted值造成并发问题
//借用对象时如果没有是否阻塞直到有对象产生
final boolean blockWhenExhausted = getBlockWhenExhausted();
//创建成功标识
boolean create;
//记录当前时间,用作记录借用操作总共花费的时间
final long waitTime = System.currentTimeMillis();
//当对象为空时一直获取
while (p == null) {
create = false;
//从双端队列弹出第一个队首对象,为空返回null
p = idleObjects.pollFirst();
//如果为空则重新创建一个对象
if (p == null) {
//创建对象
p = create();
//p==null可能对象池达到上限不能继续创建!
if (p != null) {
create = true;
}
}
//如果对象p还是为空则阻塞等待
if (blockWhenExhausted) {
if (p == null) {