对象池模式 一种特殊的工厂对象,一个对象池包含一组已经初始化过且可以使用的对象。用户可以从池中取得对象,并对其进行操作,在不需要的时候将其归还给对象池。
使用对象池的目的是为了提升性能,当需要创建比较耗费时间的对象时尤为明显。
角色和职责 对象池的管理者 管理者来维护对象池,包括初始化对象池、扩充对象池的大小、获取对象时的策略、重置归还对象的状态。
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 对象池应用浅析 一个广为人知但鲜有人用的技巧:对象池