在高并发的环境下写程序时通常碰到线程安全的问题,当然,最能想到的就是加锁,再进一步想就是池子了,所谓池子就是,里面可以放置多个同样的对象,每个线程需要用时,就从池中取走,用完时再放回到池中,即可解决线程的安全问题,又可提高速度(预先初始化)。
当然若是自己写个对象池的话,也是可以的,不过现在有个通用的apache下的commons-pool框架,个人感觉真是不错,尤其是现在我们用到的java框架里面,为我们省去了不少的开发成本。
官方网址:http://commons.apache.org/proper/commons-pool/
本文,我先只讲下通用的GenericObjectPool的原理及使用技巧。
默认实现DefaultPooledObject,里面封装了一个真正的用户需要池化的对象object。
其中DefaultPooledObject里面有两个方法,感觉非常秒
1、Exception borrowedBy :用于记录上次调用borrow时的堆栈,用于跟踪代码调用情况。
@Override
public synchronized boolean allocate() {
if (state == PooledObjectState.IDLE) {
state = PooledObjectState.ALLOCATED;
lastBorrowTime = System.currentTimeMillis();
lastUseTime = lastBorrowTime;
borrowedCount++;
if (logAbandoned) {
borrowedBy = new AbandonedObjectCreatedException();
}
return true;
} else if (state == PooledObjectState.EVICTION) {
// TODO Allocate anyway and ignore eviction test
state = PooledObjectState.EVICTION_RETURN_TO_HEAD;
return false;
}
// TODO if validating and testOnBorrow == true then pre-allocate for
// performance
return false;
}
2、Exception usedBy: 同上,记录use时的堆栈
@Override
public void use() {
lastUseTime = System.currentTimeMillis();
usedBy = new Exception("The last code to use this object was:");
}
对象的状态,也即生命周期
allocated:即为此对象被客户端使用中
idle:在池中,处于空闲状态
eviction:在实例化GenericObjectPool对象时,内部会启动一个EvictionTimer线程线程,定期(timeBetweenEvictionRunsMillis)执行evict方法,以EvictionPolicy策略destory 状态处于idle过长的对象或idle数太多的时候。
abandoned:当池中的对象被客户端拿出后,若长时间(removeAbandonedTimeout)未返回池中,或没有调用use方法,即被标记为抛弃的,当执行removeAbandoned方法时从池中destory,并若logAbandoned为true的话,则打印调用堆栈
一般需要程序员继承BasePooledObjectFactory,创建需要池化的对象,如
protected class NaviPoolableObjectFactory extends BasePooledObjectFactory {
private Class> handleClass;
private String auth;
private ServerUrl server;
public NaviPoolableObjectFactory(Class> handleClass,
ServerUrl server, String auth) {
this.handleClass = handleClass;
this.auth = auth;
this.server = server;
}
@Override
public INaviDriver create() throws Exception {
return (INaviDriver)BeanUtils.instantiateClass(handleClass
.getDeclaredConstructor(ServerUrl.class, String.class,
NaviPoolConfig.class), this.server, this.auth,
poolConfig);
}
@Override
public void destroyObject(PooledObject p) throws Exception {
p.getObject().destroy();
}
@Override
public boolean validateObject(PooledObject p) {
try{
return p.getObject().isAlive();
}catch(Exception e){
return false;
}
}
@Override
public PooledObject wrap(INaviDriver obj) {
return new DefaultPooledObject(obj);
}
}
实例化GenericObjectPool需要的配置bean
lifo:后进先出,或者先进先出
maxWaitMillis:从idle队列里面取对象时,若阻塞的话,则最大等待时长
minEvictableIdleTimeMillis:处于idle状态超过此值时,会被destory
softMinEvictableIdleTimeMillis:处于idle状态超过此值,且处于idle状态个数大于minIdle时会被destory ,正常情况下softMinEvictableIdleTimeMillis < minEvictableIdleTimeMillis
numTestsPerEvictionRun: evict线程每次遍历时evict的个数
testOnCreate:当create对象时,是否测试池化的对象是否存活
testOnBorrow: 同上,从池中borrow出来时测试
testOnReturn:同上,归还池中时测试
testWhileIdle:同上,idle状态时测试
timeBetweenEvictionRunsMillis:evict线程每次间隔时间
blockWhenExhausted:从池中取对象时,若池子用尽的话,是否阻塞等待一会maxWaitMillis
jmxEnabled:jmx是否开启监控
abandoned:被抛弃的,即被池抛弃的设置(客户端从池里面取出去后,若长时间不归还池中,则抛弃并destory)并通过配置可以设置是否logAbandoned,若true的话,则把抛弃对象时,打印相关的日志(调用日志)
对象池接口,客户端引用
GenericObjectPool 继承BaseGenericObjectPool,并实现了ObjectPool、GenericObjectPoolMXBean、UsageTracking接口
public GenericObjectPool(PooledObjectFactory factory,
GenericObjectPoolConfig config) {
super(config, ONAME_BASE, config.getJmxNamePrefix());
if (factory == null) {
jmxUnregister(); // tidy up
throw new IllegalArgumentException("factory may not be null");
}
this.factory = factory;
setConfig(config);
startEvictor(getTimeBetweenEvictionRunsMillis());
}