導言
連接是一項程序運行中比較重的資源獲取,受限于網絡的不可信等因素,創建連接的開銷一般是比較巨大的,所以才會有各種池化技術的出現,同理于線程池技術,都是為了減少創建連接的耗時,所以線程池技術基本是在高并發的業務領域必須掌握的一項技術
連接池的技術
common-pool2技術是比較常見的一種連接池,在redis的java客戶端jedis中是默認的連接池實現,在這里不鼓勵大家去自己實現一個連接池,因為現成的輪子即使有問題,大部分開源的技術也是支持修改自定義的,在大量使用中經過了大流量考驗的一種技術;所以在這里著重介紹這種常用的連接池技術
get start
首先引入maven依賴:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.3</version>
</dependency>
common-pool2的核心類是GenericObjectPool類,這個類的作用主要是用來自定義一些配置和池中對象創建的工廠方法,可以看一下具體的代碼實現:
public GenericObjectPool(PooledObjectFactory<T> 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;
idleObjects = new LinkedBlockingDeque<PooledObject<T>>(config.getFairness());
setConfig(config);
startEvictor(getTimeBetweenEvictionRunsMillis());
}
該類的構造方法有兩個參數,PooledObjectFactory是創建池中對象的工廠對象,GenericObjectPoolConfig是連接池的配置對象。
連接池配置
最能直接提升工程效率的學習就是能立竿見影的知識,在連接連接池的源碼之前可以先了解一下連接池的主要配置,看看良好的抽象是怎么做的:
maxTotal:池中對象的最大可創建數量,默認為8;
maxIdle:池中對象的最大空閑數量,默認也是8;
minIdle:池中對象的最小空閑數量,默認是0;
timeBetweenEvictionRunsMillis:空閑對象檢測線程的執行周期,單位是ms,默認-1,小于等于0則不執行檢測;
numTestsPerEvictionRun:檢測空閑對象的數量,如果小于等于0,那么每次檢測的是當前空閑數量/該配置的絕對值,,對結果向上取整;
testOnCreate:創建對象是檢測對象是否有效;默認false;
testOnBorrow:在從對象池中獲取對象時檢測對象有效性,同上;
testOnReturn:在歸還對象時檢測有效,同上;
testWhileIdle:在檢測空閑對象不需要移除時,是否對有效性進行檢測,同上;
blockWhenExhausted:當對象池沒有空閑對象時,新的獲取對象的請求是否阻塞,默認true;
maxWaitMillis:表示等待空閑連接的最大時間,如果小于等于0,那么永不超時,一直阻塞等待,只有當大于0時才會生效,超過這個時間后會拋出NoSuchElementException的異常。默認-1,單位milis。
minEvictableIdleTimeMillis:對象空閑后強制移除的時間,小于等于0會被賦值為Long.MAX,大于0時才會生效,表示這個元素空閑時間的最大值。只要超過這個時間,那么就會無視minIdle的配置移除這個對象;
softMinEvictableIdleTimeMillis:當數量大于minIdle時對象被移除的最大空閑時間,賦值規則同上;
主要方法詳細解釋
1.最重要的方法當然是創建資源的方法了:
private PooledObject<T> create() throws Exception {
//首先獲取連接最大值
int localMaxTotal = getMaxTotal();
// This simplifies the code later in this method
//如果小于0,那么復制為這
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 (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.
//如果已經超過最大計數,那么就將計數減1
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.
//如果有其他方法正在創建對象,那么就阻塞當前線程,并釋放鎖
makeObjectCountLock.wait();
}
} else {
// The pool is not at capacity. Create a new object.
//創建標志置為真,方法調用數+ 1
makeObjectCount++;
create = Boolean.TRUE;
}
}
}
//判斷是否需要創建,不需要直接返回
if (!create.booleanValue()) {
return null;
}
final PooledObject<T> p;
try {
//調用工廠方法來創建對象
p = factory.makeObject();
} catch (final Exception 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);
}
//已創建計數
createdCount.incrementAndGet();
allObjects.put(new IdentityWrapper<T>(p.getObject()), p);
return p;
}