Apache-Commons-Pool 使用笔记
Apache Commons Pool 使用笔记
Apache Commons Pool
https://commons.apache.org/proper/commons-pool/
Commons Pool 组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现,可以有效地减少处理对象池化时的工作量
commons-pool 是一个通用的池设计,其它框架可以基于commons-pool增加池的功能,它提供了池API和一些默认实现
概述
池化机制的好处
说到池我们可以联想到很多概念,比如线程池、数据库连接池、常量池等等。
在多线程设计中,可以通过池化机制复用对象以达到提高性能的目的,池的核心优势是对 对象 的复用,这样就免去了“对象重复创建”的时间,尤其当创建对象本身是一个耗时的事情,比如IO操作。
拿我们最常见的线程池为例,线程这个对象是可以被复用的,程序要执行的是任务,这些任务可以交给复用的线程来处理,而线程的创建恰恰又是一个比较耗时的操作,我们通过线程对象的池化技术达到复用线程的目的。
实现一个池要考虑哪些因素?
设计一个池的核心要素无非分成两个方面:池子 和 对象。
池子 要考虑大小,池子过大可能会占用过多的系统性能,池子过小可能由于无法获取对象而阻塞线程。当我们很确定自己需要多大的池来执行,可以使用 fixed 大小的池子,我们也可以设计一个对象个数动态变化的池子:池子有一个最大值 maxTotal 和最小值 minIdle ,最大值是对象个数的上限,当池子一段时间没有使用后,就去回收超过最小值个数的对象,这样在系统繁忙时,就可以充分复用对象,在系统空闲时,又可以释放不必要的对象。
对象 必须是可以被复用的,且创建对象应该是耗时的才值得被复用。可以被复用是一个特别重要的点,当你的对象做了一些不可复用的操作后,必须在放回池中的时候重置这些设置,或者说从池子中取出对象时,都要重新进行初始化。
minIdle/maxIdle/maxTotal
maxTotal int: 池子空间最大值,负数表示不设上限,别名:maxActive
maxIdle int: 最大的空闲对象个数,超过此值的对象放回池子的时候都会被释放
minIdle int: 最小的空闲对象个数,无论对象如何被释放,保证池子里面最少的对象个数。minIdle和许多池配置参数 initialSize 初始化大小在实现含义中有着异曲同工之用。类似Java线程池中的coreSize
Commons Pool 的3个核心概念
Commons Pool 有 3 个核心概念: 对象池 ObjectPool
, 池化对象 PooledObject
, 对象工厂 PooledObjectFactory
ObjectPool
实现对对象存取和状态管理的池实现;如:线程池、数据库连接池PooledObject
池化对象,是需要放到 ObjectPool 对象的一个包装类。添加了一些附加的信息,比如说状态信息,创建时间,激活时间,关闭时间等PooledObjectFactory
工厂类,负责生产池化对象,负责具体对象的创建、初始化,对象状态的销毁和验证
PooledObject 池化对象接口
池子中存储的对象不是你需要复用的对象,而是做了一层封装,抽象接口是 PooledObject
,为了便于构造这个封装,提供了默认包装类 DefaultPooledObject
package org.apache.commons.pool2;
import java.io.PrintWriter;
import java.util.Deque;
public interface PooledObject<T> extends Comparable<PooledObject<T>> {
T getObject();
long getCreateTime();
long getActiveTimeMillis();
long getIdleTimeMillis();
long getLastBorrowTime();
long getLastReturnTime();
long getLastUsedTime();
@Override
int compareTo(PooledObject<T> other);
@Override
boolean equals(Object obj);
@Override
int hashCode();
@Override
String toString();
boolean startEvictionTest();
boolean endEvictionTest(Deque<PooledObject<T>> idleQueue);
boolean allocate();
boolean deallocate();
void invalidate();
void setLogAbandoned(boolean logAbandoned);
void use();
void printStackTrace(PrintWriter writer);
PooledObjectState getState();
void markAbandoned();
void markReturning();
}
PooledObjectFactory 对象工厂接口
PooledObjectFactory 抽象了对象在池子生命周期中每个节点的方法
提供了一个抽象类 BasePooledObjectFactory
, 使用时直接继承此抽象类即可
public interface PooledObjectFactory<T> {
PooledObject<T> makeObject() throws Exception;
void destroyObject(PooledObject<T> p) throws Exception;
boolean validateObject(PooledObject<T> p);
void activateObject(PooledObject<T> p) throws Exception;
void passivateObject(PooledObject<T> p) throws Exception;
}
makeObject()
定义如何生成对象destroyObject()
定义如何摧毁对象,比如释放资源validateObject()
定义校验对象的方式activateObject()
初始化对象passivateObject()
重置对象
这个接口是需要用户自己去实现的
ObjectPool 对象池接口
package org.apache.commons.pool2;
import java.util.NoSuchElementException;
public interface ObjectPool<T> {
T borrowObject() throws Exception, NoSuchElementException, IllegalStateException;
void returnObject(T obj) throws Exception;
void invalidateObject(T obj) throws Exception;
void addObject() throws Exception, IllegalStateException, UnsupportedOperationException;
int getNumIdle();
int getNumActive();
void clear() throws Exception, UnsupportedOperationException;
void close();
}
borrowObject()
T borrowObject() throws Exception, NoSuchElementException, IllegalStateException;
从池中获取一个实例
返回的对象是通过 PooledObjectFactory.makeObject()
创建好的对象,或用完后归还的空闲对象。
借走的对象必须通过 returnObject(T)
或 invalidateObject(T)
归还
returnObject(T)
void returnObject(T obj) throws Exception;
把一个对象实例归还给池子,此对象必须是 borrowObject()
借出来的
invalidateObject(T obj)
void invalidateObject(T obj) throws Exception;
使一个对象变为无效,这个对象必须是 borrowObject()
借出来的
如果程序决定一个对象没用了,可以将其设为无效,从池中删除。
使用样例代码
Object obj = null;
try {
obj = pool.borrowObject();
try {
//...use the object...
} catch(Exception e) {
// invalidate the object
pool.invalidateObject(obj);
// do not return the object to the pool twice
obj = null;
} finally {
// make sure the object is returned to the pool
if(null != obj) {
pool.returnObject(obj);
}
}
} catch(Exception e) {
// failed to borrow an object
}
GenericObjectPool 使用示例
GenericObjectPool 是一个默认池子的实现,创建的时候需要提供一个对象工厂 PooledObjectFactory
, 然后通过 borrowObject()
借取对象,returnObject()
归还对象
应用要做的所有事情就是去定义如何生成对象等操作,即实现 BasePooledObjectFactory
,这是一个抽象实现,更通用的接口是 PooledObjectFactory<T>
public class CommonsPoolTest {
@Test
public void test() throws Exception {
GenericObjectPool<StringBuffer> pool = new GenericObjectPool<StringBuffer>(new BasePooledObjectFactory<StringBuffer>() {
@Override
public StringBuffer create() throws Exception {
return new StringBuffer();
}
@Override
public PooledObject<StringBuffer> wrap(StringBuffer buffer) {
return new DefaultPooledObject<StringBuffer>(buffer);
}
@Override
public void passivateObject(PooledObject<StringBuffer> pooledObject) {
pooledObject.getObject().setLength(0);
}
});
pool.setMaxTotal(8);
pool.setMaxIdle(4);
pool.setMinIdle(2);
printPoolStatus(pool);
System.out.println("pool 初始化完成\n");
StringBuffer[] buffer = new StringBuffer[9];
for (int i = 0; i < 8; i++) {
System.out.print("borrow " + i);
buffer[i] = pool.borrowObject();
buffer[i].append("string buffer " + i);
printPoolStatus(pool);
}
// 如果 borrow 第 9 个,会阻塞
// buffer[8] = pool.borrowObject();
for (int i = 0; i < 8; i++) {
System.out.print("return " + i);
pool.returnObject(buffer[i]);
printPoolStatus(pool);
}
}
private void printPoolStatus(GenericObjectPool pool) {
System.out.println(" [active: " + pool.getNumActive() + ", idle: " + pool.getNumIdle() + ", wait: " + pool.getNumWaiters() + "]");
}
}
结果
[active: 0, idle: 0, wait: 0]
pool 初始化完成
borrow 0 [active: 1, idle: 0, wait: 0]
borrow 1 [active: 2, idle: 0, wait: 0]
borrow 2 [active: 3, idle: 0, wait: 0]
borrow 3 [active: 4, idle: 0, wait: 0]
borrow 4 [active: 5, idle: 0, wait: 0]
borrow 5 [active: 6, idle: 0, wait: 0]
borrow 6 [active: 7, idle: 0, wait: 0]
borrow 7 [active: 8, idle: 0, wait: 0]
return 0 [active: 7, idle: 1, wait: 0]
return 1 [active: 6, idle: 2, wait: 0]
return 2 [active: 5, idle: 3, wait: 0]
return 3 [active: 4, idle: 4, wait: 0]
return 4 [active: 3, idle: 4, wait: 0]
return 5 [active: 2, idle: 4, wait: 0]
return 6 [active: 1, idle: 4, wait: 0]
return 7 [active: 0, idle: 4, wait: 0]
可以看到,由于设置了 maxIdle 是 4,当空闲对象多余4个时会被回收,以保证最大空闲个数是 4
GenericObjectPool池的实现原理
GenericObjectPool 内部通过阻塞队列维护一个空闲对象集合 idleObjects
,通过一个 Map 维护所有对象的映射 allObjects
。
private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects = new ConcurrentHashMap<IdentityWrapper<T>, PooledObject<T>>();
private final LinkedBlockingDeque<PooledObject<T>> idleObjects;
borrow的逻辑
判断池子是否已满是否需要阻塞,如果 idleObjects 中有空闲对象则直接取空闲对象,否则通过传入的 PooledObjectFactory 新创建一个对象,放入 allObjects map 并返回。
return的逻辑
首先判断 归还的实例是否池子中的示例,也就是是否在 allObjects 这个 map 中, 是的话重置对象,当 空闲实例个数 超过最大空闲值 maxIdle 时就会销毁这个对象,从 allObjects 和 idleObjects 中删除,并调用 PooledObjectFactory 的 destroyObject() 方法销毁对象
对象池化机制(一)池设计和commons-pool原理
https://github.com/Sayi/sayi.github.com/issues/64
对象池化机制(二)数据库连接池
https://github.com/Sayi/sayi.github.com/issues/66
JedisPool 中使用了Commons-Pool
JedisPool 中使用了 Commons-Pool 中的 GenericObjectPool
Apache Commons-pool2(整理)
https://www.jianshu.com/p/b0189e01de35
Apache Common Pool2 对象池应用浅析
https://zhuanlan.zhihu.com/p/36216932
上一篇 Lombok
下一篇 Git-WebHooks钩子
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: