对象池模式
一种特殊的工厂对象,一个对象池包含一组已经初始化过且可以使用的对象。用户可以从池中取得对象,并对其进行操作,在不需要的时候将其归还给对象池。
使用对象池的目的是为了提升性能,当需要创建比较耗费时间的对象时尤为明显。

角色和职责
对象池的管理者
管理者来维护对象池,包括初始化对象池、扩充对象池的大小、获取对象时的策略、重置归还对象的状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class PoolMgr{
private int init ;
private int max;
private int factor;
private Colection c;
public void init(){ } public Object aquire(){
return c.remove(); }
public void release(Object o){ if(o == null) return;
reset(o); c.add(o); }
}
|
对象池的使用者
使用者从管理者处获取对象,使用完成后必须归还对象避免一致占用。
1 2 3 4 5 6
| Object o = null; try{ o = PoolMgr.getInstance(); }finaly{ PoolMgr.release(o); }
|
优点
复用池中的对象,消除创建/回收对象所产生的内存开销,CPU开销以及IO开销。
常见的有:Socket的链接池、线程池、数据库连接池等。
缺点
多线程并发环境中,需要考虑线程安全问题,需要在数据结构上进行同步或者为锁竞争产生阻塞,这种开销处理不好会比创建销毁独享的开销高很多。
由于池中的对象数量有限,对象池小可能会成为性能瓶颈,太大占用内存资源高;
轻量级对象的创建/销毁不需要使用对象池,徒增系统的复杂度;
Common Pool2
Commons Pool2核心部分由三个基础接口和相关的实现类实现:
PooledObject
池化对象,对池中的对象进行封装,封装对象的状态和一些其他信息。
PooldedObject 有两个实现类,DefaultPoolObject时普通通用的实现,PooledSoftReference使用SoftReference封装了对象,供SoftReferenceObjectPool使用。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public interface PooledObject<T> extends Comparable<PooledObject<T>> { T getObject();
long getCreateTime();
long getActiveTimeMillis();
long getIdleTimeMillis();
long getLastBorrowTime();
long getLastReturnTime();
long getLastUsedTime();
void invalidate();
void use();
PooledObjectState getState();
void markAbandoned();
void markReturning(); }
|
PooledObjectFactory
池化对象工厂,负责对象的创建、初始化、销毁和验证工作,Factory对象由ObjectPool持有并使用。
因为对象的创建、初始化、销毁和仰正的工作无法通用,Commons Pool2并没有提供PooledObjectFactory的默认子类实现,只提供了抽象子类BasePooledObjectFactory
可以实现create和wrap两个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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; }
|
ObjectPool
持有对象并提供取/还等操作.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public interface ObjectPool<T> { // 从池中获取一个对象,客户端在使用完对象后必须使用 returnObject 方法返还获取的对象 T borrowObject() throws Exception, NoSuchElementException, IllegalStateException;
// 将对象返还到池中。对象必须是从 borrowObject 方法获取到的 void returnObject(T obj) throws Exception;
// 使池中的对象失效,当获取到的对象被确定无效时(由于异常或其他问题),应该调用该方法 void invalidateObject(T obj) throws Exception;
// 池中当前闲置的对象数量 int getNumIdle();
// 当前从池中借出的对象的数量 int getNumActive();
// 清除池中闲置的对象 void clear() throws Exception, UnsupportedOperationException;
// 关闭这个池,并释放与之相关的资源 void close();
... }
|
对象池配置
对象池配置提供了对象池初始化所需要的参数,Common Pool2中的基础配置类时BaseObjectPoolConfig, 有GenericObjectPoolConfig和GenericKeyedObjectPoolConfig,分别为GenericObjectPool和GenericKeyedObjectPool使用。
参数 |
解释 |
默认值 |
lifo |
连接池存放池对象的方式; true:放在空闲队列的嘴钱买呢, false:放在空闲队列的最后面 |
true |
fairness |
从池中获取/返还对象时是否使用公平锁机制 |
false |
maxWaitMillis |
获取资源的等待时间。blockWhenExhausted为true时有效。-1代表无时间限制,一致阻塞到有可用的资源 |
|
minEvictableIdleTimeMillis |
对象空闲的最小时间,打到此值后空闲对象可能会被移除,-1表示不移除 |
30分钟 |
softMinEvictableIdletimeMillis |
对象空闲的最小时间,并且池中之少保留有minidle所指定的个数 |
|
numTestsPerEvictionRun |
资源回收线程执行一次回收操作回收资源的个数 |
3 |
evictionPolicyClassName |
资源回收策略 |
org.apache.commons.pool2.impl.DefaultEvictionPolicy |
testOnCreate |
创建对象时是否调用factory.validateObject |
false |
testOnBorrow |
获取对形势是否调用factory.validateObject |
false |
testOnReturn |
返还对象时是否调用factory.validateObject |
false |
timeBetweenEvictionRunsMills |
回收资源线程的执行周期,默认-1表示不启用回收资源线程 |
-1 |
blockWhenExhausted |
资源耗尽时,是否阻塞等待获取资源 |
true |
testWhileIdel |
池中的限制对象是否由逐出器验证,无法验证的对象将从池中删除销毁 |
false |
池化对象的状态
参数值 |
描述 |
IDEL |
在池中,处于空闲状态 |
ALLOCATED |
被使用中 |
EVICTION |
正在被逐出器验证 |
VALIDAITTON |
正在验证 |
INVALID |
驱逐测试或者验证失败并将被销毁 |
ABANDONED |
对象被客户端拿出后,长时间未返回池中,或者没有调用use方法,被标记为抛弃 |
Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class StringBufferFactory extends BasePooledObjectFactory<StringBuffer> { @Override public StringBuffer create() { return new StringBuffer(); }
@Override public PooledObject<StringBuffer> wrap(StringBuffer buffer) { return new DefaultPooledObject<>(buffer); }
@Override public void passivateObject(PooledObject<StringBuffer> pooledObject) { pooledObject.getObject().setLength(0); } }
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| GenericObjectPoolConfig config = new GenericObjectPoolConfig();
PooledObjectFactory factory = new StringBufferFactory();
ObjectPool<StringBuffer> pool = new GenericObjectPool<>(factory, config); StringReader in = new StringReader("abcdefg"); StringBuffer buf = null; try { buf = pool.borrowObject();
for (int c = in.read(); c != -1; c = in.read()) { buf.append((char) c); } return buf.toString(); } catch (Exception e) { try { pool.invalidateObject(buf); buf = null; } catch (Exception ex) { } throw e; } finally { try { in.close(); } catch (Exception e) { }
try { if (null != buf) { pool.returnObject(buf); } } catch (Exception e) { } }
|
参考:
Apache Common Pool2 对象池应用浅析
一个广为人知但鲜有人用的技巧:对象池