平时开发涉及到比如数据库连接、Redis连接等等其他连接,连接在创建、销毁的过程往往比较消耗资源、时间,于是,便出现连接池的概念。
其中不同的连接池都是保存着对象的,只不过有的对象是数据库连接对象,有的对象是Redis连接对象,由此可见,只要我们理解了对象池,则不同的池我们也掌握了原理。
我们基于apache开源的common-pools包来掌握对象池(GenericObjectPool)的原理。
对象池,主要有三个方法
一、borrowObject()
借一个对象。
二、returnObject(T obj)
归还对象,正常使用完对象后,归还对象。
三、invalidateObject(T obj)
废弃对象,在使用对象过程中,对象出现异常(不是指我们的业务代码产生的异常),我们往往需要丢弃掉这个对象,调用这个方法,对象池就可以销毁该对象。
--idleObjects.hasTakeWaiters()获取当前有多少个线程阻塞着在等待空闲对象
在创建对象池时,我们可以设置阻塞blockWhenExhausted属性,默认为true。
根据是否阻塞?分两个不同策略
1.阻塞
从空闲队列取出队头元素,
如果取到的元素为null,则说明没空闲元素;创建一个对象
如果创建对象为null,则表示创建对象数量已经达到上限,只能阻塞直到空闲队列有可用的元素
进行阻塞:
如果maxWaitMillis(毫秒)小于0则一直阻塞直到空闲队列有可用的元素;
如果maxWaitMillis小于等于0则阻塞maxWaitMillis毫秒,从空闲队列取出头元素。
如果取到元素还是null,则抛出异常:等待空闲对象超时。
如果取到元素不是null,则调用boolean PooledObject.allocate()对对象进行分配
如果分配结果为false,则对象分配不成功,对象依然设为null
2.不阻塞
从空闲队列取出队头元素,
如果取到的元素为空,则说明没空闲元素;创建一个对象
如果创建对象为null,则表示创建对象数量已经达到上限,抛出异常:池的对象被全部用完。
如果取到元素不是null,则调用boolean PooledObject.allocate()对对象进行分配
如果分配结果为false,则对象分配不成功,对象依然设为null
通过1或者2取得对象元素,如果获取到对象不为null,执行下面操作:
调用PooledObjectFactory.activateObject(PooledObject<T> p)激活对象,如果activateObject抛出异常,则销毁对象p,且是新创建的对象则抛出异常:激活对象失败
激活对象后,如果testOnBorrow为true或者是新创建的对象且testOnCreate为true,则
调用boolean PooledObjectFactory.validateObject(PooledObject<T> p)验证对象是不是有效的、 可用的,如果验证为false,则销毁对象p,并确认空闲队列中至少有一个空闲对象(调用GenericObjectPool.ensureIdle(1, false) )
通过上面的过程后,如果对象为null,则循环上面步骤,直到获取对象不为null或者抛出异常;
最后调用方法updateStatsBorrow(p, System.currentTimeMillis() - waitTime)--待留
返回对象
/over.
从allObjects中获取封装对象的value对象(就是对象T obj的PooledObject对象,假设为p);
如果p为null,则说明已经被删除了,不做什么操作,直接返回结束方法;
加锁(p){
如果p状态不是分配状态,则抛出异常:对象已经归还或不可用
状态是分配状态,则调用PooledObject.markReturning()将p设置为正在归还状态。
}
if(testOnReturn){
调用boolean PooledObjectFactory.validateObject(PooledObject<T> p)验证对象是不是可用的,如果验证为false,则销毁对象p,并确认空闲队列中至少有一个空闲对象(调用GenericObjectPool.ensureIdle(1, false) )
}
调用PooledObjectFactory.passivateObject(PooledObject<T> p) 钝化对象,调用有异常抛出则销毁对象p,并确认空闲队列中至少有一个空闲对象(调用GenericObjectPool.ensureIdle(1, false) )
调用PooledObject.deallocate()将自己设为空闲状态,设置失败则抛出异常:对象已经被归还或是不可用的
如果空闲对象数量不少于maxIdle,则不需要归还该对象,则销毁该对象;否则,根据lifo值将对象加入空闲队列的队头或队尾。
/over.
从allObjects中获取封装对象的value对象(就是对象T obj的PooledObject对象,假设为p);
如果p为null则抛出异常:对象已经不在池中;
否则,
加锁(p){
检查p是不是INVALID状态,不是则销毁对象;是INVALID状态,则表示对象已经在销毁中,直接结束方法。
}
确认空闲队列中至少有一个空闲对象(调用GenericObjectPool.ensureIdle(1, false) )
/over.
++++++++++++++++++++++++++++++++++++++++++++++
GenericObjectPool<T>类
++++++++++++++++++++++++++++++++++++++++++++++
=================
创建一个对象
=================
PooledObject<T> create():
如果创建对象的数量已经大于maxTotal,或者大于int的最大值(2^31-1,2147483647),则对象数量达到上限,不能创建对象,返回null;
调用PooledObjectFactory<T>.makeOjbect()方法创建对象,如果创建失败,则抛出异常;
创建对象成功,则将新对象put进入allObjects(ConcurrentHashMap对象),返回新对象。
=================
=================
销毁对象
=================
destroy(PooledObject<T> toDestory):
对象调用PooledObject.invalidate()方法将自己作废,即将状态设为state = PooledObjectState.INVALID;
从空闲队列中删除该对象;
allObjects map中删除该对象;
调用工厂对象PooledObjectFactory.destroyObject(PooledObject<T> p)销毁对象,可以在这里释放相关资源等等工作。
=================
确认至少有idleCount个空闲对象
=================
void ensureIdle(int idleCount, boolean always):
always为true则表示不管有没有在阻塞等待的线程(idleObjects.hasTakeWaiters()),都会检查空闲对象的数量,否则,方法结束返回
如果空闲对象数量少于idleCount,则创建新对象放进空闲队列,根据lifo值来放进队列的队头或队尾。
++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++
PooledObject
++++++++++++++++++++++++++++++++++++++++++++++
=================
分配
=================
synchronized boolean allocate()://对象同步方法
如果对象状态为空闲PooledObjectState.IDLE,则
修改状态为分配PooledObjectState.ALLOCATED,
修改最近借时间和用时间lastBorrowTime = lastBorrowTime = now.
对象借次数递增borrowedCount++;
返回true;
如果对象状态为PooledObjectState.EVICTION(暂译:驱逐),则状态改为PooledObjectState.EVICTION_RETURN_TO_HEAD,返回false;
其他情况都返回false。
=================
归还
=================
void markReturning():
将状态设置归还
state = PooledObjectState.RETURNING;
borrowObject
..
其中不同的连接池都是保存着对象的,只不过有的对象是数据库连接对象,有的对象是Redis连接对象,由此可见,只要我们理解了对象池,则不同的池我们也掌握了原理。
我们基于apache开源的common-pools包来掌握对象池(GenericObjectPool)的原理。
对象池,主要有三个方法
一、borrowObject()
借一个对象。
二、returnObject(T obj)
归还对象,正常使用完对象后,归还对象。
三、invalidateObject(T obj)
废弃对象,在使用对象过程中,对象出现异常(不是指我们的业务代码产生的异常),我们往往需要丢弃掉这个对象,调用这个方法,对象池就可以销毁该对象。
--idleObjects.hasTakeWaiters()获取当前有多少个线程阻塞着在等待空闲对象
一、borrowObject()
##获取池中的对象GenericObjectPool.borrowObject()##在创建对象池时,我们可以设置阻塞blockWhenExhausted属性,默认为true。
根据是否阻塞?分两个不同策略
1.阻塞
从空闲队列取出队头元素,
如果取到的元素为null,则说明没空闲元素;创建一个对象
如果创建对象为null,则表示创建对象数量已经达到上限,只能阻塞直到空闲队列有可用的元素
进行阻塞:
如果maxWaitMillis(毫秒)小于0则一直阻塞直到空闲队列有可用的元素;
如果maxWaitMillis小于等于0则阻塞maxWaitMillis毫秒,从空闲队列取出头元素。
如果取到元素还是null,则抛出异常:等待空闲对象超时。
如果取到元素不是null,则调用boolean PooledObject.allocate()对对象进行分配
如果分配结果为false,则对象分配不成功,对象依然设为null
2.不阻塞
从空闲队列取出队头元素,
如果取到的元素为空,则说明没空闲元素;创建一个对象
如果创建对象为null,则表示创建对象数量已经达到上限,抛出异常:池的对象被全部用完。
如果取到元素不是null,则调用boolean PooledObject.allocate()对对象进行分配
如果分配结果为false,则对象分配不成功,对象依然设为null
通过1或者2取得对象元素,如果获取到对象不为null,执行下面操作:
调用PooledObjectFactory.activateObject(PooledObject<T> p)激活对象,如果activateObject抛出异常,则销毁对象p,且是新创建的对象则抛出异常:激活对象失败
激活对象后,如果testOnBorrow为true或者是新创建的对象且testOnCreate为true,则
调用boolean PooledObjectFactory.validateObject(PooledObject<T> p)验证对象是不是有效的、 可用的,如果验证为false,则销毁对象p,并确认空闲队列中至少有一个空闲对象(调用GenericObjectPool.ensureIdle(1, false) )
通过上面的过程后,如果对象为null,则循环上面步骤,直到获取对象不为null或者抛出异常;
最后调用方法updateStatsBorrow(p, System.currentTimeMillis() - waitTime)--待留
返回对象
/over.
二、returnObject(T obj)
##归还对象到池中GenericObjectPool.returnObject(T obj)##从allObjects中获取封装对象的value对象(就是对象T obj的PooledObject对象,假设为p);
如果p为null,则说明已经被删除了,不做什么操作,直接返回结束方法;
加锁(p){
如果p状态不是分配状态,则抛出异常:对象已经归还或不可用
状态是分配状态,则调用PooledObject.markReturning()将p设置为正在归还状态。
}
if(testOnReturn){
调用boolean PooledObjectFactory.validateObject(PooledObject<T> p)验证对象是不是可用的,如果验证为false,则销毁对象p,并确认空闲队列中至少有一个空闲对象(调用GenericObjectPool.ensureIdle(1, false) )
}
调用PooledObjectFactory.passivateObject(PooledObject<T> p) 钝化对象,调用有异常抛出则销毁对象p,并确认空闲队列中至少有一个空闲对象(调用GenericObjectPool.ensureIdle(1, false) )
调用PooledObject.deallocate()将自己设为空闲状态,设置失败则抛出异常:对象已经被归还或是不可用的
如果空闲对象数量不少于maxIdle,则不需要归还该对象,则销毁该对象;否则,根据lifo值将对象加入空闲队列的队头或队尾。
/over.
三、invalidateObject(T obj)
##废弃对象GenericObjectPool.invalidateObject(T obj)##从allObjects中获取封装对象的value对象(就是对象T obj的PooledObject对象,假设为p);
如果p为null则抛出异常:对象已经不在池中;
否则,
加锁(p){
检查p是不是INVALID状态,不是则销毁对象;是INVALID状态,则表示对象已经在销毁中,直接结束方法。
}
确认空闲队列中至少有一个空闲对象(调用GenericObjectPool.ensureIdle(1, false) )
/over.
++++++++++++++++++++++++++++++++++++++++++++++
GenericObjectPool<T>类
++++++++++++++++++++++++++++++++++++++++++++++
=================
创建一个对象
=================
PooledObject<T> create():
如果创建对象的数量已经大于maxTotal,或者大于int的最大值(2^31-1,2147483647),则对象数量达到上限,不能创建对象,返回null;
调用PooledObjectFactory<T>.makeOjbect()方法创建对象,如果创建失败,则抛出异常;
创建对象成功,则将新对象put进入allObjects(ConcurrentHashMap对象),返回新对象。
=================
=================
销毁对象
=================
destroy(PooledObject<T> toDestory):
对象调用PooledObject.invalidate()方法将自己作废,即将状态设为state = PooledObjectState.INVALID;
从空闲队列中删除该对象;
allObjects map中删除该对象;
调用工厂对象PooledObjectFactory.destroyObject(PooledObject<T> p)销毁对象,可以在这里释放相关资源等等工作。
=================
确认至少有idleCount个空闲对象
=================
void ensureIdle(int idleCount, boolean always):
always为true则表示不管有没有在阻塞等待的线程(idleObjects.hasTakeWaiters()),都会检查空闲对象的数量,否则,方法结束返回
如果空闲对象数量少于idleCount,则创建新对象放进空闲队列,根据lifo值来放进队列的队头或队尾。
++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++
PooledObject
++++++++++++++++++++++++++++++++++++++++++++++
=================
分配
=================
synchronized boolean allocate()://对象同步方法
如果对象状态为空闲PooledObjectState.IDLE,则
修改状态为分配PooledObjectState.ALLOCATED,
修改最近借时间和用时间lastBorrowTime = lastBorrowTime = now.
对象借次数递增borrowedCount++;
返回true;
如果对象状态为PooledObjectState.EVICTION(暂译:驱逐),则状态改为PooledObjectState.EVICTION_RETURN_TO_HEAD,返回false;
其他情况都返回false。
=================
归还
=================
void markReturning():
将状态设置归还
state = PooledObjectState.RETURNING;
++++++++++++++++++++++++++++++++++++++++++++++
四、源码
borrowObject
public T borrowObject(long borrowMaxWaitMillis) throws Exception {
assertOpen();
AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
(getNumIdle() < 2) &&
(getNumActive() > getMaxTotal() - 3) ) {
removeAbandoned(ac);
}
PooledObject<T> p = null;
// Get local copy of current config so it is consistent for entire
// method execution
boolean blockWhenExhausted = getBlockWhenExhausted();
boolean create;
long waitTime = System.currentTimeMillis();
while (p == null) {
create = false;
if (blockWhenExhausted) {
p = idleObjects.pollFirst();
if (p == null) {
p = create();
if (p != null) {
create = true;
}
}
if (p == null) {
if (borrowMaxWaitMillis < 0) {
p = idleObjects.takeFirst();
} else {
p = idleObjects.pollFirst(borrowMaxWaitMillis,
TimeUnit.MILLISECONDS);
}
}
if (p == null) {
throw new NoSuchElementException(
"Timeout waiting for idle object");
}
if (!p.allocate()) {
p = null;
}
} else {
p = idleObjects.pollFirst();
if (p == null) {
p = create();
if (p != null) {
create = true;
}
}
if (p == null) {
throw new NoSuchElementException("Pool exhausted");
}
if (!p.allocate()) {
p = null;
}
}
if (p != null) {
try {
factory.activateObject(p);
} catch (Exception e) {
try {
destroy(p);
} catch (Exception e1) {
// Ignore - activation failure is more important
}
p = null;
if (create) {
NoSuchElementException nsee = new NoSuchElementException(
"Unable to activate object");
nsee.initCause(e);
throw nsee;
}
}
if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
boolean validate = false;
Throwable validationThrowable = null;
try {
validate = factory.validateObject(p);
} catch (Throwable t) {
PoolUtils.checkRethrow(t);
validationThrowable = t;
}
if (!validate) {
try {
destroy(p);
destroyedByBorrowValidationCount.incrementAndGet();
} catch (Exception e) {
// Ignore - validation failure is more important
}
p = null;
if (create) {
NoSuchElementException nsee = new NoSuchElementException(
"Unable to validate object");
nsee.initCause(validationThrowable);
throw nsee;
}
}
}
}
}
updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
return p.getObject();
}
returnObject(T obj)
public void returnObject(T obj) {
PooledObject<T> p = allObjects.get(new IdentityWrapper<T>(obj));
if (p == null) {
if (!isAbandonedConfig()) {
throw new IllegalStateException(
"Returned object not currently part of this pool");
} else {
return; // Object was abandoned and removed
}
}
synchronized(p) {
final PooledObjectState state = p.getState();
if (state != PooledObjectState.ALLOCATED) {
throw new IllegalStateException(
"Object has already been returned to this pool or is invalid");
} else {
p.markReturning(); // Keep from being marked abandoned
}
}
long activeTime = p.getActiveTimeMillis();
if (getTestOnReturn()) {
if (!factory.validateObject(p)) {
try {
destroy(p);
} catch (Exception e) {
swallowException(e);
}
try {
ensureIdle(1, false);
} catch (Exception e) {
swallowException(e);
}
updateStatsReturn(activeTime);
return;
}
}
try {
factory.passivateObject(p);
} catch (Exception e1) {
swallowException(e1);
try {
destroy(p);
} catch (Exception e) {
swallowException(e);
}
try {
ensureIdle(1, false);
} catch (Exception e) {
swallowException(e);
}
updateStatsReturn(activeTime);
return;
}
if (!p.deallocate()) {
throw new IllegalStateException(
"Object has already been returned to this pool or is invalid");
}
int maxIdleSave = getMaxIdle();
if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
try {
destroy(p);
} catch (Exception e) {
swallowException(e);
}
} else {
if (getLifo()) {
idleObjects.addFirst(p);
} else {
idleObjects.addLast(p);
}
if (isClosed()) {
// Pool closed while object was being added to idle objects.
// Make sure the returned object is destroyed rather than left
// in the idle object pool (which would effectively be a leak)
clear();
}
}
updateStatsReturn(activeTime);
}
invalidateObject(T obj)
public void invalidateObject(T obj) throws Exception {
PooledObject<T> p = allObjects.get(new IdentityWrapper<T>(obj));
if (p == null) {
if (isAbandonedConfig()) {
return;
} else {
throw new IllegalStateException(
"Invalidated object not currently part of this pool");
}
}
synchronized (p) {
if (p.getState() != PooledObjectState.INVALID) {
destroy(p);
}
}
ensureIdle(1, false);
}
..