1. 什么是對象池?
??????我們都知道一個對象,比如car其生命周期大致可分為“創建”,“使用”, “銷毀”三個階段,如果每次創建、使用完一個對象就釋放掉,等到需要新的的對象再重新創建,當創建一個對象的成本比較大時(比如數據庫連接等),這樣會非常消耗資源。為了節約系統資源,就需要就這些之前創建過的對象保存下來,等待下次需要時直接使用,這種用于充當保存對象的“容器”就是對象池,它使得對象可復用,減少頻繁創建造成的開銷。
2. 對象池原理
?????試想如果讓你來實現一個對象池,應該怎么做呢? 首先,對象池肯定是用來管理對象的,舉個例子,我們可以把對象看成是一臺公共自行車,將對象池比作一個自行車站點,首先,對于這個站點的功能,肯定有‘借’自行車,“還”自行車,定期“檢查”自行車車況,“銷毀”(壞了的,拿去修或者銷毀)這幾個基本的功能。但站點一般只負責管理車,不會自己造自行車,還需要一個自行車工廠去生產車,而這個造自行車的工廠除了生產車,還需要在對車進行“檢測”、“出庫”功能、“回收”等功能。
????? jedis使用的pool是apache實現的對象池,它是否和我們想象的一樣?大同小異?。≡谄鋚ool組件中,對象池化工作被劃分了三類對象: PooledObjectFactory, ObjectPool, ObjectPoolFactory。
2.1 PooledObjectFactory
?????PooledObjectFactory用于管理被池化的對象的產生、激活、掛起、校驗和銷毀,它是一個接口,使用者根據是什么工廠來具體實現。
public interface PooledObjectFactory<T> {
// 用于產生新的對象
PooledObject<T> makeObject() throws Exception;
// 用于銷毀被validateObject判定為已失效的對象
void destroyObject(PooledObject<T> p) throws Exception;
// 用于校驗一個具體的對象是否仍然有效,如果對象失效會被destroyObject 方法銷毀
boolean validateObject(PooledObject<T> p);
// 將對象重新設置為初始狀態
void activateObject(PooledObject<T> p) throws Exception;
// 將這個對象掛起,設置為休眠狀態
void passivateObject(PooledObject<T> p) throws Exception;
}
??????jedis自定義的Factory為JedisFactory:
class JedisFactory implements PooledObjectFactory<Jedis> {
private final AtomicReference<HostAndPort> hostAndPort = new AtomicReference<HostAndPort>();
private final int connectionTimeout;
private final int soTimeout;
private final String password;
private final int database;
private final String clientName;
public JedisFactory(final String host, final int port, final int connectionTimeout,
final int soTimeout, final String password, final int database, final String clientName) {
this.hostAndPort.set(new HostAndPort(host, port));
this.connectionTimeout = connectionTimeout;
this.soTimeout = soTimeout;
this.password = password;
this.database = database;
this.clientName = clientName;
}
public JedisFactory(final URI uri, final int connectionTimeout, final int soTimeout,
final String clientName) {
if (!JedisURIHelper.isValid(uri)) {
throw new InvalidURIException(String.format(
"Cannot open Redis connection due invalid URI. %s", uri.toString()));
}
this.hostAndPort.set(new HostAndPort(uri.getHost(), uri.getPort()));
this.connectionTimeout = connectionTimeout;
this.soTimeout = soTimeout;
this.password = JedisURIHelper.getPassword(uri);
this.database = JedisURIHelper.getDBIndex(uri);
this.clientName = clientName;
}
public void setHostAndPort(final HostAndPort hostAndPort) {
this.hostAndPort.set(hostAndPort);
}
@Override
public void activateObject(PooledObject<Jedis> pooledJedis) throws Exception {
final BinaryJedis jedis = pooledJedis.getObject();
if (jedis.getDB() != database) {
jedis.select(database);
}
}
@Override
public void destroyObject(PooledObject<Jedis> pooledJedis) throws Exception {
final BinaryJedis jedis = pooledJedis.getObject();
if (jedis.isConnected()) {
try {
try {
jedis.quit();
} catch (Exception e) {
}
jedis.disconnect();
} catch (Exception e) {
}
}
}
@Override
public PooledObject<Jedis> makeObject() throws Exception {
final HostAndPort hostAndPort = this.hostAndPort.get();
final Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout,
soTimeout);
try {
jedis.connect();
if (null != this.password) {
jedis.auth(this.password);
}
if (database != 0) {
jedis.select(database);
}
if (clientName != null) {
jedis.clientSetname(clientName);
}
} catch (JedisException je) {
jedis.close();
throw je;
}
return new DefaultPooledObject<Jedis>(jedis);
}
@Override
public void passivateObject(PooledObject<Jedis> pooledJedis) throws Exception {
// TODO maybe should select db 0? Not sure right now.
}
@Override
public boolean validateObject(PooledObject<Jedis> pooledJedis) {
final BinaryJedis jedis = pooledJedis.getObject();
try {
HostAndPort hostAndPort = this.hostAndPort.get();
String connectionHost = jedis.getClient().getHost();
int connectionPort = jedis.getClient().getPort();
return hostAndPort.getHost().equals(connectionHost)
&& hostAndPort.getPort() == connectionPort && jedis.isConnected()
&& jedis.ping().equals("PONG");
} catch (final Exception e) {
return false;
}
}
}
2.2 ObjectPool
????? ObjectPool用于管理要被池化的對象的借出和歸還,它也是在org.apache.commons.pool包中定義的一個接口,它有多種實現,GenericObjectPool是其中的一種,而jedis使用的對象池就是這個pool。
一個對象池往往需要配置很多合適的參數才能使用,對GenericObjectPool的配置是通過org.apache.commons.pool.impl.GenericObjectPool.Config來完成,這是個簡單的數值對象,每個成員都預設了默認值,這里我們詳細介紹一下里面的各個成員的含義。
2.1 GenericObjectPool 配置詳情
- maxActive 控制池中對象的最大數量。默認值是8,如果是負值表示沒限制。
- maxIdle 控制池中空閑的對象的最大數量。默認值是8,如果是負值表示沒限制。
- minIdle
控制池中空閑的對象的最小數量。默認值是0。 - whenExhaustedAction 指定池中對象被消耗完以后的行為,有下面這些選擇:
WHEN_EXHAUSTED_FAIL 0
WHEN_EXHAUSTED_BLOCK 1
????如果是WHEN_EXHAUSTED_FAIL,當池中對象達到上限以后,繼續borrowObject會拋出NoSuchElementException異常。
????如果是WHEN_EXHAUSTED_BLOCK,當池中對象達到上限以后,會一直等待,直到有一個對象可用。這個行為還與maxWait有關,如果maxWait是正數,那么會等待maxWait的毫秒的時間,超時會拋出NoSuchElementException異常;如果maxWait為負值,會永久等待。
????whenExhaustedAction 的默認值是WHEN_EXHAUSTED_BLOCK,maxWait的默認值是-1。
- maxWaitMillis 如果whenExhaustedAction 是WHEN_EXHAUSTED_BLOCK,指定等待的毫秒數。如果maxWait是正數,那么會等待maxWait的毫秒的時間,超時會拋出NoSuchElementException異常;如果maxWait為負值,會永久等待。
- testOnBorrow 如果testOnBorrow被設置,pool會在borrowObject返回對象之前使用PoolableObjectFactory的validateObject來驗證這個對象是否有效,要是對象沒通過驗證,這個對象會被丟棄,然后重新創造一個新的對象。testOnBorrow的默認值是false。
- testOnReturn 如果testOnReturn被設置,pool會在returnObject的時候通過PoolableObjectFactory的validateObject方法驗證對象,如果對象沒通過驗證,對象會被丟棄,不會被放到池中。testOnReturn的默認值是false。
- testWhileIdle 在檢測空閑對象線程檢測到對象不需要移除時,是否檢測對象的有效性。true是,默認值是false。
這個設置僅在timeBetweenEvictionRunsMillis被設置成正值(>0)的時候才會生效。 testWhileIdle的默認值是false。 - timeBetweenEvictionRunsMillis 空閑對象檢測線程的執行周期,即多長時候執行一次空閑對象檢測。單位是毫秒數。如果小于等于0,則不執行檢測線程。默認值是-1;
2.2 代碼講解
只看參數根本看不出它們都是做什么的,還是得從代碼入手,先看其uml圖:
可以看到GenericObjectPool繼承了BaseGenericObjectPool,實現了ObjectPool等接口,使用泛型T來指定緩存對象的類型。BaseGenericObjectPool主要做了一些公共的實現,GenericObjectPool則是復寫了一些抽象方法,做具體的實現。
public GenericObjectPool(final PooledObjectFactory<T> factory) {
this(factory, new GenericObjectPoolConfig<T>());
}
public GenericObjectPool(final PooledObjectFactory<T> factory,
final GenericObjectPoolConfig<T> 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<>(config.getFairness());
// 設置參數
setConfig(config);
}
public LinkedBlockingDeque(final boolean fairness) {
this(Integer.MAX_VALUE, fairness);
}
public LinkedBlockingDeque(final int capacity, final boolean fairness) {
if (capacity <= 0) {
throw new IllegalArgumentException();
}
this.capacity = capacity;
lock = new InterruptibleReentrantLock(fairness);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
可以看到,構造GenericObjectPool的關鍵參數是PooledObjectFactory,是對象池的使用者自己需要實現的用于產生池化對象的工廠,對于jedis而言,該工廠就是前面介紹的JedisFactory。可以看出對象池和工廠是解耦的,對象池只負責管理對象,至于生產、激活、銷毀等由具體的工廠提供。
GenericObjectPool創建了一個雙端隊列LinkedBlockingDeque作為idle隊列緩存所有空閑的對象。而BlockingDeque的鎖競爭,默認使用非公平鎖。
public void setConfig(GenericObjectPoolConfig conf) {
setLifo(conf.getLifo());
setMaxIdle(conf.getMaxIdle());
setMinIdle(conf.getMinIdle());
setMaxTotal(conf.getMaxTotal());
setMaxWaitMillis(conf.getMaxWaitMillis());
setBlockWhenExhausted(conf.getBlockWhenExhausted());
setTestOnCreate(conf.getTestOnCreate());
setTestOnBorrow(conf.getTestOnBorrow());
setTestOnReturn(conf.getTestOnReturn());
setTestWhileIdle(conf.getTestWhileIdle());
setNumTestsPerEvictionRun(conf.getNumTestsPerEvictionRun());
setMinEvictableIdleTimeMillis(conf.getMinEvictableIdleTimeMillis());
setTimeBetweenEvictionRunsMillis(
conf.getTimeBetweenEvictionRunsMillis());
setSoftMinEvictableIdleTimeMillis(
conf.getSoftMinEvictableIdleTimeMillis());
setEvictionPolicyClassName(conf.getEvictionPolicyClassName());
}
public final void setTimeBetweenEvictionRunsMillis(
final long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
startEvictor(timeBetweenEvictionRunsMillis);
}
在參數設置setTimeBetweenEvictionRunsMillis方法里會開啟一個TimerTask對idle隊列進行定時掃描,必要時進行淘汰
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);
}
}
}
startEvictor主要用于創建新的Evictor,然后基于ScheduledThreadPoolExecutor進行線程調度。
class Evictor extends TimerTask {
@Override
public void run() {
ClassLoader savedClassLoader =
Thread.currentThread().getContextClassLoader();
try {
if (factoryClassLoader != null) {
// Set the class loader for the factory
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(Exception e) {
swallowException(e);
} catch(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 (Exception e) {
swallowException(e);
}
} finally {
// Restore the previous CCL
Thread.currentThread().setContextClassLoader(savedClassLoader);
}
}
}
Evictor的run方法主要調用了evict()方法
// 對空閑的連接進行淘汰清理
public void evict() throws Exception {
assertOpen();
if (idleObjects.size() > 0) {
PooledObject<T> underTest = null;
// 獲取淘汰策略
final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();
synchronized (evictionLock) {
final EvictionConfig evictionConfig = new EvictionConfig(
getMinEvictableIdleTimeMillis(),
getSoftMinEvictableIdleTimeMillis(),
getMinIdle());
final boolean testWhileIdle = getTestWhileIdle();
// 為一次淘汰策略運行掃描多少個對象
for (int i = 0, m = getNumTests(); i < m; i++) {
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;
}
// 將當前空閑的連接設置為淘汰狀態,如果該連接不是空閑狀態則重新迭代一個
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;
}
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);
}
}
- numTestsPerEvictionRun 設置驅逐線程每次檢測對象的數量。
這個設置僅在timeBetweenEvictionRunsMillis被設置成正值(>0)的時候才會生效。numTestsPerEvictionRun的默認值是3。 - minEvictableIdleTimeMillis 指定最小的空閑驅逐的時間間隔(空閑超過指定的時間的對象,會被清除掉)。這個設置僅在timeBetweenEvictionRunsMillis被設置成正值(>0)的時候才會生效。minEvictableIdleTimeMillis默認值是30分鐘。
- softMinEvictableIdleTimeMillis 與minEvictableIdleTimeMillis類似,也是指定最小的空閑驅逐的時間間隔(空閑超過指定的時間的對象,會被清除掉),不過會參考minIdle的值,只有idle對象的數量超過minIdle的值,對象才會被清除。這個設置僅在timeBetweenEvictionRunsMillis被設置成正值(>0)的時候才會生效,并且這個配置能被minEvictableIdleTimeMillis配置取代(minEvictableIdleTimeMillis配置項的優先級更高)。
softMinEvictableIdleTimeMillis的默認值是-1。 - lifo pool可以被配置成LIFO隊列(last-in-first-out)或FIFO隊列(first-in-first-out),來指定空閑對象被使用的次序。lifo的默認值是true。
驅逐線程采取的淘汰策略是默認策略:
public class DefaultEvictionPolicy<T> implements EvictionPolicy<T> {
@Override
public boolean evict(final EvictionConfig config, final PooledObject<T> underTest,
final int idleCount) {
// 當對象的空閑時間大于SoftMinEvictableIdleTimeMillis并且實際空閑對象數目大于配置的空閑對象時會被淘汰
// 或者時間大約IdleEvictTime 也會被淘汰
if ((config.getIdleSoftEvictTime() < underTest.getIdleTimeMillis() &&
config.getMinIdle() < idleCount) ||
config.getIdleEvictTime() < underTest.getIdleTimeMillis()) {
return true;
}
return false;
}
}
空閑淘汰線程默認是不開啟的,是可有可無,接下來我們重點來看下對象的借、取和歸還。
?????要想從對象池獲取一個對象,GenericObjectPool提供的接口是borrowObject:
@Override
public T borrowObject() throws Exception {
// maxWaitMillis 當獲取不到對象需要等待的時間
return 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) {
// 池里已經沒有空閑的對象,需要創建一個,可能創建成功也可能失敗
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");
}
}
if (!p.allocate()) { // 判斷當前的狀態是否是idle,若是則設置為allocated
p = null;
}
if (p != null) { // 獲取到對象后,進行一系列的校驗操作
try {
// 激活對象
factory.activateObject(p);
} catch (final Exception e) {
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對對象進行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;
}
}
}
}
}
// 更新一些統計信息
updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
return p.getObject();
}
從borrowObject方法可以看出,對象是采取懶加載的方式進行創建,一開始對象池并沒有對象,在從對象池取對象時方才create
private PooledObject<T> create() throws Exception {
int localMaxTotal = getMaxTotal();
// This simplifies the code later in this method
if (localMaxTotal < 0) {
localMaxTotal = Integer.MAX_VALUE;
}
final long localStartTimeMillis = System.currentTimeMillis();
final long localMaxWaitTimeMillis = Math.max(getMaxWaitMillis(), 0);
// 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 (create == null) {
synchronized (makeObjectCountLock) {
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.
// 超過了容量,回滾下
createCount.decrementAndGet();
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 {
// 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.
// 如果正在創建的對象數目不是0,這些創建可能失敗,所以可以等待一下,重新測試
makeObjectCountLock.wait(localMaxWaitTimeMillis);
}
} else {
// The pool is not at capacity. Create a new object.
// 沒有達到容量就直接創建
makeObjectCount++;
create = Boolean.TRUE;
}
}
// Do not block more if maxWaitTimeMillis is set.
// 那么設置maxWaitTimeMillis 有什么意義呢?
if (create == null &&
(localMaxWaitTimeMillis > 0 &&
System.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis)) {
create = Boolean.FALSE;
}
}
if (!create.booleanValue()) {
return null;
}
final PooledObject<T> p;
try {
// 工廠創建對象
p = factory.makeObject();
} catch (final Throwable e) {
// 創建失敗就回滾
createCount.decrementAndGet();
throw e;
} finally {
synchronized (makeObjectCountLock) {
// 釋放鎖,將正在創建對象的統計數量-1 并喚醒其他等待線程
makeObjectCount--;
makeObjectCountLock.notifyAll();
}
}
final AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getLogAbandoned()) {
p.setLogAbandoned(true);
// TODO: in 3.0, this can use the method defined on PooledObject
if (p instanceof DefaultPooledObject<?>) {
((DefaultPooledObject<T>) p).setRequireFullStackTrace(ac.getRequireFullStackTrace());
}
}
// 增加創建的數量
createdCount.incrementAndGet();
// 對象創建成功后放到allObjects Map中 注意不是idle Map
allObjects.put(new IdentityWrapper<>(p.getObject()), p);
return p;
}
可以看到,新創建的對象一開始并不是放在idle隊列中,只有在對象return的時候才返回到idle隊列,新創建的對象再borrowObject時被設置為allocated狀態
public synchronized boolean allocate() {
if (state == PooledObjectState.IDLE) {
// 將對象設置為Allocated狀態
state = PooledObjectState.ALLOCATED;
// 設置借出時間
lastBorrowTime = System.currentTimeMillis();
lastUseTime = lastBorrowTime;
borrowedCount++;
if (logAbandoned) {
borrowedBy.fillInStackTrace();
}
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;
}
最后再來看下returnObject流程:
public void returnObject(final T obj) {
// 獲取歸還對象對應的PooledObject
final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
if (p == null) {
if (!isAbandonedConfig()) {
throw new IllegalStateException(
"Returned object not currently part of this pool");
}
return; // Object was abandoned and removed
}
// 確保對象是Allocated狀態,并將對象設置為歸還中
markReturningState(p);
final long activeTime = p.getActiveTimeMillis();
// 判斷對象在歸還時是否需要test
if (getTestOnReturn() && !factory.validateObject(p)) {
try {
// 需要驗證,但驗證不通過則銷毀該對象
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
try {
// 如果歸還的對象驗證沒通過,重新創建一個新的idle對象
ensureIdle(1, false);
} catch (final Exception e) {
swallowException(e);
}
updateStatsReturn(activeTime);
return;
}
try {
// 掛起對象,JedisFactory這個方法do Nothing
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;
}
// 將對象狀態設為idle
if (!p.deallocate()) {
throw new IllegalStateException(
"Object has already been returned to this pool or is invalid");
}
final int maxIdleSave = getMaxIdle();
if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
// 如果對象池關閉了或者超過容量了 直接銷毀對象
try {
destroy(p);
} catch (final 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);
}
2.3 ObjectPoolFactory
????? ObjectPoolFactory 用于大量生成相同類型和設置的ObjectPool,使用工廠模式來產生ObjectPool.避免每個地方重寫一次調用相應構造方法的代碼。它是一個在org.apache.commons.pool中定義的接口,定義了一個ObjectPool createPool()方法,
public static void main(String[] args) {
Object obj = null;
PoolableObjectFactory factory = new PoolableObjectFactorySample();
ObjectPoolFactory poolFactory = new StackObjectPoolFactory(factory);
ObjectPool pool = poolFactory.createPool();
try {
for(long i = 0; i < 100 ; i++) {
System.out.println("== " + i + " ==");
obj = pool.borrowObject();
System.out.println(obj);
pool.returnObject(obj);
}
obj = null;
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try{
if (obj != null) {
pool.returnObject(obj);
}
pool.close();
}
catch (Exception e){
e.printStackTrace();
}
}
}
3. 總結
????本文首先介紹了為什么要使用對象池? 然后分析了對象池的基本原理和三大組件PoolableObjectFactory、 ObjectPool、ObjectPoolFactory,其中詳細闡述了ObjectPool的一個具體實現類GenericObjectPool及其配置說明。恰當地使用對象池化,可以有效地降低頻繁生成某些對象所造成的開銷,從而提高整體的性能,而借助Apache Common Pool組件,可以有效地減少Jedis花在處理對象池化上的工作量。