之前學習了一下Jedis的操作原理和JedisPool的相關實現,但是在JedisPool的實現中對于JedisPool的連接池實現沒有深入的學習,因為JedisPool的連接池的實現是基于Apache Commons-pool的,所以只要重點關注Apache Commons-pool就OK了,之前在看Mybatis的連接池的實現時候有關注DBCP的連接池的實現,DBCP的連接池就是靠Apache Commons-pool來支撐的,所以這里也算是把DBCP底層的連接池也學習了一下。
Apache Commons-pool是一個Apache組織開源的對象池。我們所熟知的基本上就是DBCP和JedisPool了。對于這種“池”的概念Apache Commons-pool做了很好的支撐,也能盡量的將使用方和對象池本身做一個很好的解耦。目前Apache Commons-pool托管在github上,地址是:
Apache Commons-pool整體的代碼結構并不復雜,下面是代碼結構:

主要關鍵的類都在pool2包下,impl里面是一些默認的實現,proxy是提供的一些代理的支持。
對于用過JedisPool的童鞋來說呢,對于一個類一定不陌生,那就是GenericObjectPoolConfig,最開始用的時候我也是被各種參數弄得暈頭轉向,歸根結底還是對pool的概念的模糊。本文的目的就是弄懂原理,弄清楚各個參數都是干嘛用的。
首先看一些關鍵的參數和說明吧:
lifo:對象池存儲空閑對象是使用的LinkedBlockingDeque,它本質上是一個支持FIFO和FILO的雙向的隊列,common-pool2中的LinkedBlockingDeque不是Java原生的隊列,而有common-pool2重新寫的一個雙向隊列。如果為true,表示使用FIFO獲取對象。默認值是true.建議使用默認值。
fairness:common-pool2實現的LinkedBlockingDeque雙向阻塞隊列使用的是Lock鎖。這個參數就是表示在實例化一個LinkedBlockingDeque時,是否使用lock的公平鎖。默認值是false,建議使用默認值。
maxWaitMillis:當沒有空閑連接時,獲取一個對象的最大等待時間。如果這個值小于0,則永不超時,一直等待,直到有空閑對象到來。如果大于0,則等待maxWaitMillis長時間,如果沒有空閑對象,將拋出NoSuchElementException異常。默認值是-1;可以根據需要自己調整,單位是毫秒。
minEvictableIdleTimeMillis:對象最小的空閑時間。如果為小于等于0,最Long的最大值,如果大于0,當空閑的時間大于這個值時,執(zhí)行移除這個對象操作。默認值是1000L * 60L * 30L;即30分鐘。這個參數是強制性的,只要空閑時間超過這個值,就會移除。
softMinEvictableIdleTimeMillis:對象最小的空間時間,如果小于等于0,取Long的最大值,如果大于0,當對象的空閑時間超過這個值,并且當前空閑對象的數量大于最小空閑數量(minIdle)時,執(zhí)行移除操作。這個和上面的minEvictableIdleTimeMillis的區(qū)別是,它會保留最小的空閑對象數量。而上面的不會,是強制性移除的。默認值是-1;
numTestsPerEvictionRun:檢測空閑對象線程每次檢測的空閑對象的數量。默認值是3;如果這個值小于0,則每次檢測的空閑對象數量等于當前空閑對象數量除以這個值的絕對值,并對結果向上取整。
testOnCreate:在創(chuàng)建對象時檢測對象是否有效,true是,默認值是false。
testOnBorrow:在從對象池獲取對象時是否檢測對象有效,true是;默認值是false。
testOnReturn:在向對象池中歸還對象時是否檢測對象有效,true是,默認值是false。
testWhileIdle:在檢測空閑對象線程檢測到對象不需要移除時,是否檢測對象的有效性。true是,默認值是false。
timeBetweenEvictionRunsMillis:空閑對象檢測線程的執(zhí)行周期,即多長時候執(zhí)行一次空閑對象檢測。單位是毫秒數。如果小于等于0,則不執(zhí)行檢測線程。默認值是-1;
blockWhenExhausted:當對象池沒有空閑對象時,新的獲取對象的請求是否阻塞。true阻塞。默認值是true;
maxTotal:對象池中管理的最多對象個數。默認值是8。
maxIdle:對象池中最大的空閑對象個數。默認值是8。
minIdle:對象池中最小的空閑對象個數。默認值是0。
其實光看參數,根本看不出它們都是做什么的,所以我們還是開始看代碼,我們主要關注的類是GenericObjectPool,雖然Commons-pool提供了基于key的Object的緩存池,但是我們不常用:
GenericObjectPool的UML類圖如下:

其中GenericObjectPool繼承了BaseGenericObjectPool,實現了ObjectPool、GenericObjectPoolMXBean等接口,并且提供了泛型T來指定緩存對象的類型。
BaseGenericObjectPool中主要做了一些公共的實現,GenericObjectPool則是復寫一些抽象方法,做具體的實現,GenericObjectPoolMXBean主要做一些JMX的監(jiān)控。
/**
* Create a new <code>GenericObjectPool</code> using defaults from
* {@link GenericObjectPoolConfig}.
*
* @param factory The object factory to be used to create object instances
* used by this pool
*/
public GenericObjectPool(final PooledObjectFactory<T> factory) {
this(factory, new GenericObjectPoolConfig());
}
/**
* Create a new <code>GenericObjectPool</code> using a specific
* configuration.
*
*
* factory用來給對象池創(chuàng)建對象
* @param factory The object factory to be used to create object instances
* used by this pool
*
* 對相應的對象池進行配置
* @param config The configuration to use for this pool instance. The
* configuration is used by value. Subsequent changes to
* the configuration object will not be reflected in the
* pool.
*/
public GenericObjectPool(final PooledObjectFactory<T> factory,
final 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;
//初始化空閑對象池,傳入鎖的模式,公平還是非公平
idleObjects = new LinkedBlockingDeque<PooledObject<T>>(config.getFairness());
//參數設置
setConfig(config);
//開啟淘汰機制,timeBetweenEvictionRunsMillis為淘汰的間隔
startEvictor(getTimeBetweenEvictionRunsMillis());
}
這里比較關鍵的就是PooledObjectFactory,PooledObjectFactory是外部使用對象池的使用者需要實現的關于待緩存的對象的創(chuàng)建、激活銷毀等一系列操作的工廠。這也充分體現了“解耦”的思想,自己的東西自己管,很合理。
主要方法如下:
makeObject為創(chuàng)建對象的方法。
destroyObject為銷毀對象的方法。
validateObject為校驗對象有消息的方法。
activateObject為激活對象的方法。
passivateObject為鈍化對象的方法。
其實就是在創(chuàng)建對象的時候利用Factoty的makeObject來創(chuàng)建,銷毀的時候利用destroyObject來銷毀.....
接下來呢創(chuàng)建了一個雙端隊列LinkedBlockingDeque作為idle隊列緩存所有空閑的對象,顯然是無界的,那么大小就需要外部去控制。這里有一個點就是BlockingDeque的鎖競爭,默認是非公平的鎖,這也是JDK默認選擇的鎖。吞吐量很重要哦。
fairness就是鎖的類型。
接下來開啟一個TimeTask對idle隊列進行定時的掃描,必要時進行淘汰。
timeBetweenEvictionRunsMillis參數就是設置掃描周期的,這也和上面的參數說明相對應。
/**
* <p>Starts the evictor with the given delay. If there is an evictor
* running when this method is called, it is stopped and replaced with a
* new evictor with the specified delay.</p>
*
* <p>This method needs to be final, since it is called from a constructor.
* See POOL-195.</p>
*
* @param delay time in milliseconds before start and between eviction runs
*
* 進行同步控制保證evictor為單例
*/
final void startEvictor(final long delay) {
synchronized (evictionLock) {
if (null != evictor) {
EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis, TimeUnit.MILLISECONDS);
evictor = null;
evictionIterator = null;
}
if (delay > 0) {
evictor = new Evictor();
//開啟淘汰任務
EvictionTimer.schedule(evictor, delay, delay);
}
}
}
這里主要做Evictor的創(chuàng)建,EvictionTimer是一個基于ScheduledThreadPoolExecutor進行線程調度的。
/**
* The idle object evictor {@link TimerTask}.
* 空閑對象的淘汰任務
* @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis
*/
class Evictor extends TimerTask {
/**
* Run pool maintenance. Evict objects qualifying for eviction and then
* ensure that the minimum number of idle instances are available.
* Since the Timer that invokes Evictors is shared for all Pools but
* pools may exist in different class loaders, the Evictor ensures that
* any actions taken are under the class loader of the factory
* associated with the pool.
*/
@Override
public void run() {
//創(chuàng)建class Loader 來保證對象是在一個ClassLoader中
final ClassLoader savedClassLoader =
Thread.currentThread().getContextClassLoader();
try {
if (factoryClassLoader != null) {
// Set the class loader for the factory
final ClassLoader cl = factoryClassLoader.get();
if (cl == null) {
// The pool has been dereferenced and the class loader
// GC'd. Cancel this timer so the pool can be GC'd as
// well.
cancel();
return;
}
Thread.currentThread().setContextClassLoader(cl);
}
// Evict from the pool
try {
//進行淘汰
evict();
} catch(final Exception e) {
swallowException(e);
} catch(final OutOfMemoryError oome) {
// Log problem but give evictor thread a chance to continue
// in case error is recoverable
oome.printStackTrace(System.err);
}
// Re-create idle instances.
try {
ensureMinIdle();
} catch (final Exception e) {
swallowException(e);
}
} finally {
// Restore the previous CCL
Thread.currentThread().setContextClassLoader(savedClassLoader);
}
}
}
這里調用了evict();方法,這個方法的實現是在子類中的:
/**
* {@inheritDoc}
* <p>
* Successive activations of this method examine objects in sequence,
* cycling through objects in oldest-to-youngest order.
*
* 進行空閑連接的淘汰
*/
@Override
public void evict() throws Exception {
assertOpen();
//判斷空閑的線程池是否為空,為空那么不進行淘汰
if (idleObjects.size() > 0) {
PooledObject<T> underTest = null;
//獲取淘汰策略,默認是DefaultEvictionPolicy
final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();
synchronized (evictionLock) {
//淘汰的配置設置
final EvictionConfig evictionConfig = new EvictionConfig(
getMinEvictableIdleTimeMillis(),//最小的空閑時間
getSoftMinEvictableIdleTimeMillis(),
getMinIdle());//最小的空閑數
//是否對空閑的連接進行Test
final boolean testWhileIdle = getTestWhileIdle();
//getNumTests為一次淘汰策略的運行掃描多少對象
for (int i = 0, m = getNumTests(); i < m; i++) {
//是否要創(chuàng)建淘汰的迭代器
if (evictionIterator == null || !evictionIterator.hasNext()) {
evictionIterator = new EvictionIterator(idleObjects);
}
//如果當前的迭代器是空的,那么證明當前的空閑對象池已經用光了,那么不進行淘汰
if (!evictionIterator.hasNext()) {
// Pool exhausted, nothing to do here
return;
}
try {
underTest = evictionIterator.next();
} catch (final NoSuchElementException nsee) {
// Object was borrowed in another thread
// Don't count this as an eviction test so reduce i;
i--;
evictionIterator = null;
continue;
}
//將當前的空閑的連接設置為淘汰狀態(tài),不為空閑則重新迭代出一個
if (!underTest.startEvictionTest()) {
// Object was borrowed in another thread
// Don't count this as an eviction test so reduce i;
i--;
continue;
}
// User provided eviction policy could throw all sorts of
// crazy exceptions. Protect against such an exception
// killing the eviction thread.
boolean evict;
try {
//根據淘汰策略判斷是否需要淘汰
evict = evictionPolicy.evict(evictionConfig, underTest,
idleObjects.size());
} catch (final Throwable t) {
// Slightly convoluted as SwallowedExceptionListener
// uses Exception rather than Throwable
PoolUtils.checkRethrow(t);
swallowException(new Exception(t));
// Don't evict on error conditions
evict = false;
}
//如果需要淘汰,那么執(zhí)行淘汰邏輯
if (evict) {
destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
} else {//如果不需要淘汰,判斷是否進行對象有效期的校驗
if (testWhileIdle) {
boolean active = false;
try {
//對對象進行激活
factory.activateObject(underTest);
active = true;
} catch (final Exception e) {
destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
}
if (active) {
//對對象的有效性進行檢測
if (!factory.validateObject(underTest)) {
destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
} else {
try {
factory.passivateObject(underTest);
} catch (final Exception e) {
destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
}
}
}
}
if (!underTest.endEvictionTest(idleObjects)) {
// TODO - May need to add code here once additional
// states are used
}
}
}
}
}
final AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
removeAbandoned(ac);
}
}
這里邏輯比較長,做了下面幾件事:
- 判斷空閑對象池大小,大于0才允許淘汰。
- 接下來獲取一系列的配置,其中testWhileIdle主要用于判斷是否進行空閑檢查。
- 根據numTestsPerEvictionRun確定一次淘汰要檢查的空閑對象的數目。
- 創(chuàng)建evictionIterator迭代器,迭代出一個待檢查的對象,并判斷對象的狀態(tài),如果狀態(tài)不為空閑狀態(tài),那么返回重新選一個空閑的對象。如果是空閑狀態(tài)那么設置為EVICTION。
- 獲取淘汰機制,默認是DefaultEvictionPolicy:
public class DefaultEvictionPolicy<T> implements EvictionPolicy<T> {
@Override
public boolean evict(final EvictionConfig config, final PooledObject<T> underTest,
final int idleCount) {
//首先獲取
if ((config.getIdleSoftEvictTime() < underTest.getIdleTimeMillis() &&
config.getMinIdle() < idleCount) ||
config.getIdleEvictTime() < underTest.getIdleTimeMillis()) {
return true;
}
return false;
}
}
這里主要涉及到之前的幾個配置:
MinEvictableIdleTimeMillis,SoftMinEvictableIdleTimeMillis和MinIdle
根據上面代碼我們可知:
當對象的空閑時間大于SoftMinEvictableIdleTimeMillis并且空閑對象數小于實際的空閑線程時,會進行淘汰?;蛘呖臻e時間大于IdleEvictTime時會淘汰,也就是說上面的情況會保證當前最小對象數,也就是所描述的“soft”
- 如果需要淘汰,那么執(zhí)行destroy方法進行對象淘汰。如果不需要淘汰,并且設置了空閑檢查,那么進行一系列的檢查操作,首先進行activateObject來激活對象,接著利用validateObject判斷對象的有效性,最后鈍化對象passivateObject。
- 將檢查對象狀態(tài)設置為空閑。
這就是整個空閑淘汰的過程。
相比較于核心功能——對象緩存,空閑淘汰還是閑的可有可無,畢竟不是核心功能。接下來看下對象的獲取的實現:
如果想從對象池中獲取一個對象,需要調用borrowObject方法:
/**
* Equivalent to <code>{@link #borrowObject(long)
* borrowObject}({@link #getMaxWaitMillis()})</code>.
* <p>
* {@inheritDoc}
*/
@Override
public T borrowObject() throws Exception {
return borrowObject(getMaxWaitMillis());
}
這里可以看到用到了一個參數maxWaitMillis,這個參數主要是設置如果獲取不到對象最大需要等待的時間。核心實現在borrowObject(getMaxWaitMillis())里:
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
assertOpen();
final 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
//當資源池耗盡的時候是否阻塞住
final boolean blockWhenExhausted = getBlockWhenExhausted();
boolean create;
final long waitTime = System.currentTimeMillis();
while (p == null) {
create = false;
p = idleObjects.pollFirst();//彈出一個對象
if (p == null) {//如果為空,那么新創(chuàng)建,這里也可能創(chuàng)建成功,也可能創(chuàng)建失敗
p = create();
if (p != null) {
create = true;
}
}
//如果空閑對象耗盡的情況下是否阻塞等待
if (blockWhenExhausted) {
if (p == null) {
//沒有設置超時時間的話,那么利用take阻塞到有資源為止
if (borrowMaxWaitMillis < 0) {
p = idleObjects.takeFirst();
} else {
//等待borrowMaxWaitMillis
p = idleObjects.pollFirst(borrowMaxWaitMillis,
TimeUnit.MILLISECONDS);
}
}
if (p == null) {
//超時未獲取,拋出獲取不到的異常
throw new NoSuchElementException(
"Timeout waiting for idle object");
}
} else {
//如果耗盡的時候不阻塞等待,那么直接拋出異常
if (p == null) {
throw new NoSuchElementException("Pool exhausted");
}
}
//判斷當前的對象的狀態(tài)是否為idle
if (!p.allocate()) {
p = null;
}
//如果獲取到對象,那么進行對象的一系列的校驗等操作
if (p != null) {
try {
//激活對象
factory.activateObject(p);
} catch (final Exception e) {
//異常則執(zhí)行銷毀操作
try {
destroy(p);
} catch (final Exception e1) {
// Ignore - activation failure is more important
}
p = null;
if (create) {
final NoSuchElementException nsee = new NoSuchElementException(
"Unable to activate object");
nsee.initCause(e);
throw nsee;
}
}
//這里主要是獲取testOnBorrow和testOnCreate,那么在對象是從idle池中取出來的或者是新創(chuàng)建的要進行Test
if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
boolean validate = false;
Throwable validationThrowable = null;
try {
//檢測有效性
validate = factory.validateObject(p);
} catch (final Throwable t) {
PoolUtils.checkRethrow(t);
validationThrowable = t;
}
//如果無效的話進行銷毀操作
if (!validate) {
try {
destroy(p);
destroyedByBorrowValidationCount.incrementAndGet();
} catch (final Exception e) {
// Ignore - validation failure is more important
}
p = null;
if (create) {
final NoSuchElementException nsee = new NoSuchElementException(
"Unable to validate object");
nsee.initCause(validationThrowable);
throw nsee;
}
}
}
}
}
//更新一些統(tǒng)計信息
updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
return p.getObject();
}
整個流程和ThreadPoolExecutor獲取任務差不多,但是中間還摻雜了創(chuàng)建新的對象的邏輯,這里有幾個參數:
borrowMaxWaitMillis:對象池空閑時最大的等待時間。
blockWhenExhausted:在對象池耗盡的時候是否要進行等待。
testOnBorrow:borrow對象的時候是否要檢測。
testOnCreate:新創(chuàng)建對象的時候是否要進行檢測。
大體流程我們看明白了,但是總感覺少點什么。對象什么時候創(chuàng)建?活躍的對象是怎么表示的?
首先,對象采用懶加載的形式進行創(chuàng)建,在調用create()方法時進行創(chuàng)建:
/**
* Attempts to create a new wrapped pooled object.
* <p>
* If there are {@link #getMaxTotal()} objects already in circulation
* or in process of being created, this method returns null.
*
* @return The new wrapped pooled object
*
* @throws Exception if the object factory's {@code makeObject} fails
*/
private PooledObject<T> create() throws Exception {
//獲取最大的對象數
int localMaxTotal = getMaxTotal();
// This simplifies the code later in this method
if (localMaxTotal < 0) {
localMaxTotal = Integer.MAX_VALUE;
}
// Flag that indicates if create should:
// - TRUE: call the factory to create an object
// - FALSE: return null
// - null: loop and re-test the condition that determines whether to
// call the factory
Boolean create = null;
//這里采用while去做創(chuàng)建和JDK的線程池實現類似
while (create == null) {
synchronized (makeObjectCountLock) {
//增加創(chuàng)建數
final long newCreateCount = createCount.incrementAndGet();
//和最大的對象數想比較
if (newCreateCount > localMaxTotal) {
//如果超限,回滾下
// The pool is currently at capacity or in the process of
// making enough new objects to take it to capacity.
//減小創(chuàng)建數
createCount.decrementAndGet();
//正在創(chuàng)建的Object數目,正在創(chuàng)建的為0,那么也就不用等待了
if (makeObjectCount == 0) {
// There are no makeObject() calls in progress so the
// pool is at capacity. Do not attempt to create a new
// object. Return and wait for an object to be returned
create = Boolean.FALSE;
} else {
//如果正在創(chuàng)建的對象數不為0,意味著目前可能對象池還沒有用超限,并且有失敗的,那么進行重試
// There are makeObject() calls in progress that might
// bring the pool to capacity. Those calls might also
// fail so wait until they complete and then re-test if
// the pool is at capacity or not.
makeObjectCountLock.wait();
}
} else {
//創(chuàng)建新對象
// The pool is not at capacity. Create a new object.
makeObjectCount++;
create = Boolean.TRUE;
}
}
}
//如果沒有新的對象創(chuàng)建,那么返回
if (!create.booleanValue()) {
return null;
}
//創(chuàng)建新對象
final PooledObject<T> p;
try {
p = factory.makeObject();
} catch (Exception e) {
createCount.decrementAndGet();
throw e;
} finally {
//釋放鎖,并且喚醒其他的等待線程,表明當前已經有線程創(chuàng)建好了對象了
synchronized (makeObjectCountLock) {
makeObjectCount--;
makeObjectCountLock.notifyAll();
}
}
final AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getLogAbandoned()) {
p.setLogAbandoned(true);
}
//增加創(chuàng)建的數量
createdCount.incrementAndGet();
//將新創(chuàng)建的對象添加到Map中
allObjects.put(new IdentityWrapper<T>(p.getObject()), p);
return p;
}
新對象創(chuàng)建這里比較有意思,首先要判斷當前的對象數有沒有超過對象總量的限制,如果超限了,那么就不能創(chuàng)建新對象了,然后通過makeObjectCount判斷有沒有正在創(chuàng)建對象的線程在創(chuàng)建對象,makeObjectCount只是一個long類型,這里主要通過makeObjectCountLock來保證可見性,如果當前有線程在創(chuàng)建對象那么wait()一下,因為這里存在創(chuàng)建失敗的問題,如果之前的創(chuàng)建失敗,可能會導致newCreateCount短暫的超過最大上限。通過makeObject創(chuàng)建新對象。創(chuàng)建之后喚醒等待的線程。
這里出現了一個allObjects,它是ConcurrentHashMap,新創(chuàng)建的對象不直接加入到idle隊列中,而是加入到ConcurrentHashMap中,只有在對象return的時候才返回到idle隊列中。新創(chuàng)建的對象被標示為ALLOCATED狀態(tài)。并且緩存在ConcurrentHashMap中。
/**
* Allocates the object.
*
* @return {@code true} if the original state was {@link PooledObjectState#IDLE IDLE}
*/
@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;
}
這樣borrowObject的流程就結束了,這里主要比較重要的就是,新創(chuàng)建的對象是緩存在ConcurrentHashMap中的而不是idle隊列。
接下來看下returnObject,將整體的流程走通:
/**
* {@inheritDoc}
* <p>
* If {@link #getMaxIdle() maxIdle} is set to a positive value and the
* number of idle instances has reached this value, the returning instance
* is destroyed.
* <p>
* If {@link #getTestOnReturn() testOnReturn} == true, the returning
* instance is validated before being returned to the idle instance pool. In
* this case, if validation fails, the instance is destroyed.
* <p>
* Exceptions encountered destroying objects for any reason are swallowed
* but notified via a {@link SwallowedExceptionListener}.
*/
@Override
public void returnObject(final T obj) {
//從ConcurrentHashMap中獲取相應的PooledObject
final 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");
}
return; // Object was abandoned and removed
}
synchronized(p) {
final PooledObjectState state = p.getState();
//確定返回的對象都是ALLOCATED狀態(tài)的
if (state != PooledObjectState.ALLOCATED) {
throw new IllegalStateException(
"Object has already been returned to this pool or is invalid");
}
//將狀態(tài)設置為RETURNING
p.markReturning(); // Keep from being marked abandoned
}
final long activeTime = p.getActiveTimeMillis();
//判斷在對象返回的時候是否需要Test
if (getTestOnReturn()) {
if (!factory.validateObject(p)) {
try {
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
try {
ensureIdle(1, false);
} catch (final Exception e) {
swallowException(e);
}
updateStatsReturn(activeTime);
return;
}
}
//鈍化
try {
factory.passivateObject(p);
} catch (final Exception e1) {
swallowException(e1);
try {
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
try {
ensureIdle(1, false);
} catch (final Exception e) {
swallowException(e);
}
updateStatsReturn(activeTime);
return;
}
//將對象狀態(tài)設置為IDLE
if (!p.deallocate()) {
throw new IllegalStateException(
"Object has already been returned to this pool or is invalid");
}
//獲取最大的空閑的對象池大小
final int maxIdleSave = getMaxIdle();
//如果對象池Close,或者超限了,那么直接銷毀對象
if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
try {
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
} else {
//進行空閑對象入隊,這里主要是判斷是否是FIFO的模式
if (getLifo()) {
idleObjects.addFirst(p);
} else {
idleObjects.addLast(p);
}
//如果close了,那么清理所有對象
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);
}
這里比較關鍵的點是,對象的返還,是先到ConcurrentHashMap中找到對象,并且標示它的狀態(tài),最后轉換為IDLE,最后返還給IDLE隊列中。也就是說ConcurrentHashMap維護了所有的對象,而IDLE隊列只是維護空閑的對象。
這樣整個Commons-pool主干流程就分析完了,這里其實效果不是太好,最好是結合JedisPool和DBCP來看,同時也有一些邊角的東西不能完全看到,如果有遺漏的東西,還請指出。