commons-pool2原始碼走讀(一) 池物件PooledObject介面及其實現
阿新 • • 發佈:2018-12-31
commons-pool2原始碼走讀(一) 池物件PooledObject<T>介面及其實現
PooledObject<T>用來定義池物件的一個wrapper 介面,用於跟蹤物件的附加資訊,比如狀態、建立時間、使用時間等。這個類的實現必須是執行緒安全的。
1. 介面定義
PooledObject<T>繼承自Comparable<PooledObject<T>>,表明池物件是可以排序的。該介面定義了大量的方法用來獲取一個池物件的諸多資訊。
該介面原始碼如下
public interface PooledObject <T> extends Comparable<PooledObject<T>> {
//返回被包裝的實際物件
T getObject();
//返回該物件的建立時間
long getCreateTime();
//以毫秒為單位獲得該物件最後在活動狀態中使用的時間(它可能仍然處於活動狀態,在這種情況下後續呼叫將返回一個增加的值)。
long getActiveTimeMillis();
//以毫秒為單位獲得該物件最後在空閒狀態中花費的時間(它可能仍然處於空閒狀態,在這種情況下,後續呼叫將返回一個增加的值)。
long getIdleTimeMillis();
//上次借用時間
long getLastBorrowTime();
//上次歸還時間
long getLastReturnTime();
/**
* 返回上次使用時間的一個估計值,如果Pooled Object實現了TrackedUse介面
* 那麼返回值將是TrackedUse.getLastUsed()和getLastBorrowTime()的較大者,
* 否則返回值和getLastBorrowTime()相等
*/
long getLastUsedTime();
@Override
int compareTo(PooledObject<T> other);
@Override
boolean equals(Object obj);
@Override
int hashCode();
@Override
String toString();
//嘗試將池物件置於PooledObjectState.EVICTION狀態
boolean startEvictionTest();
boolean endEvictionTest(Deque<PooledObject<T>> idleQueue);
//分配該物件,如果源狀態為PooledObjectState.IDLE空閒狀態則返回true,
//同時將狀態改為PooledObjectState.ALLOCATED,該狀態表明物件正在被使用
boolean allocate();
//與上面一個方法作用相反,將PooledObjectState.ALLOCATED置為PooledObjectState.IDLE
boolean deallocate();
//將物件置為PooledObjectState.INVALID無效狀態
void invalidate();
//設定是否記錄物件使用的堆疊資訊,可用於池洩漏時問題追溯
void setLogAbandoned(boolean logAbandoned);
void use();
//列印物件的呼叫堆疊資訊
void printStackTrace(PrintWriter writer);
//返回物件目前的狀態
PooledObjectState getState();
//標記該物件發生了洩漏
void markAbandoned();
//標記該物件正在被歸還到物件池
void markReturning();
}
2.狀態列舉PooledObjectState
在上面的介面中,大多數的方法都用來更改物件的狀態,根據物件的狀態可以推斷出該物件是否正在使用,以及接下來可以進行什麼樣的操作。物件狀態如下:
public enum PooledObjectState {
/**
* 在佇列中,未被使用,空閒狀態。呼叫allocate()方法後物件應該被置於該狀態
*/
IDLE,
/**
* 已分配,在使用中,呼叫allocate()方法後應該講物件置於該狀態
*/
ALLOCATED,
/**
* 在佇列中, 當前正在測試,可能會被回收。在此狀態被借出後狀態回被置為EVICTION_RETURN_TO_HEAD
*/
EVICTION,
/**
* 不在佇列中。當借用該物件時發現物件,發現正在進行回收測試,故將EVICTION更
* 改EVICTION_RETURN_TO_HEAD,表明曾經在回收過程中被借出,在回收完後它應該從新新增到佇列的頭部。
*/
EVICTION_RETURN_TO_HEAD,
/**
* 在佇列中,目前正在進行校驗
*/
VALIDATION,
/**
* 不在佇列中,當前正在驗證。當物件從池中被借出,
* 在配置了testOnBorrow的情況下,對像從佇列移除和進行預分配的時候會進行驗證(借用時校驗)
*/
VALIDATION_PREALLOCATED,
/**
* 不在佇列中,正在進行驗證。從池中借出物件時,發現物件正在進行校驗,並將物件狀態改為該狀態
*/
VALIDATION_RETURN_TO_HEAD,
/**
*無效的,並且將要或已經被銷燬。
*/
INVALID,
/**
* 洩漏的
*/
ABANDONED,
/**
*歸還中,呼叫markReturning()方法會將物件狀態改為此狀態,表明正在歸還一個物件
*/
RETURNING
}
3.預設實現DefaultPooledObject
DefaultPooledObject是PooledObject<T> 的預設實現,用該類能滿足絕大多數需求。
下面看看原始碼是怎麼實現的:
public class DefaultPooledObject<T> implements PooledObject<T> {
//將實際物件定義為final防止併發
private final T object;
//初始預設狀態為空閒狀態
private PooledObjectState state = PooledObjectState.IDLE;
/**
* 初始化各種時間為系統當前時間
* 可以看到將不變的(createTime)設定為final,而降需要變化的各種變數設定為volatile避免併發問題
*/
private final long createTime = System.currentTimeMillis();
private volatile long lastBorrowTime = createTime;
private volatile long lastUseTime = createTime;
private volatile long lastReturnTime = createTime;
private volatile boolean logAbandoned = false;
//CallStack記錄堆疊資訊
private volatile CallStack borrowedBy = NoOpCallStack.INSTANCE;
private volatile CallStack usedBy = NoOpCallStack.INSTANCE;
//記錄總共被借用次數
private volatile long borrowedCount = 0;
public DefaultPooledObject(final T object) {
this.object = object;
}
@Override
public T getObject() {
return object;
}
@Override
public long getCreateTime() {
return createTime;
}
/**
* 獲取物件上次最大活躍時間
*
* @return
*/
@Override
public long getActiveTimeMillis() {
// 先將物件的值進行copy,避免併發問題
final long rTime = lastReturnTime;
final long bTime = lastBorrowTime;
//活躍時間=上次歸還時間-上次借用時間,如還未歸還則用當前時間-上次借用時間
if (rTime > bTime) {
return rTime - bTime;
}
return System.currentTimeMillis() - bTime;
}
/**
* 獲取物件空閒時間
*
* @return
*/
@Override
public long getIdleTimeMillis() {
final long elapsed = System.currentTimeMillis() - lastReturnTime;
// 當出現如下情況時間可能是負的:
// 1.在計算的時候另一個執行緒更改了lastReturnTime(因為lastReturnTime是volatile)
// 2.伺服器的系統時間可能不準確
return elapsed >= 0 ? elapsed : 0;
}
@Override
public long getLastBorrowTime() {
return lastBorrowTime;
}
@Override
public long getLastReturnTime() {
return lastReturnTime;
}
public long getBorrowedCount() {
return borrowedCount;
}
/**
* 獲取物件上次使用時間
*
* @return
*/
@Override
public long getLastUsedTime() {
//返回上次使用時間的一個估計值,如果Pooled Object實現了TrackedUse介面
// 那麼返回值將是TrackedUse.getLastUsed()和getLastBorrowTime()的較大者,
// 否則返回值和getLastBorrowTime()相等
if (object instanceof TrackedUse) {
return Math.max(((TrackedUse) object).getLastUsed(), lastUseTime);
}
return lastUseTime;
}
@Override
public int compareTo(final PooledObject<T> other) {
final long lastActiveDiff = this.getLastReturnTime() - other.getLastReturnTime();
if (lastActiveDiff == 0) {
//如果兩個物件的上次歸還時間相等,則用identityHashCode根據存地址產生的hash值判斷物件的順序
return System.identityHashCode(this) - System.identityHashCode(other);
}
// 如果不相等則返回lastActiveDiff,考慮返回值是int而lastActiveDiff是long避免整數溢位
return (int) Math.min(Math.max(lastActiveDiff, Integer.MIN_VALUE), Integer.MAX_VALUE);
}
@Override
public String toString() {
final StringBuilder result = new StringBuilder();
result.append("Object: ");
result.append(object.toString());
result.append(", State: ");
synchronized (this) {
result.append(state.toString());
}
return result.toString();
// TODO add other attributes
}
/**
* 回收物件時,先呼叫startEvictionTest進行標記該物件狀態為回收中,避免被其它執行緒借出後使用
* 回收測試完後呼叫endEvictionTest方法
*
* @return
*/
@Override
public synchronized boolean startEvictionTest() {
//呼叫此方法將空閒物件設定為回收狀態,然後進行校驗是否能被回收,只有當物件是空閒狀態IDLE時才進行回收
if (state == PooledObjectState.IDLE) {
state = PooledObjectState.EVICTION;
return true;
}
return false;
}
/**
* 回收物件時,先呼叫startEvictionTest進行標記該物件狀態為回收中,避免被其它執行緒借出後使用
* 回收測試完後呼叫endEvictionTest方法,重新將物件設定為空閒狀態
*
* @return
*/
@Override
public synchronized boolean endEvictionTest(
final Deque<PooledObject<T>> idleQueue) {
//回收測試完該物件後呼叫該方法重新設定物件狀態為空閒狀態
//如果物件是PooledObjectState.EVICTION,表明物件還在佇列中(未被borrow,當被borrow後狀態會被改為EVICTION_RETURN_TO_HEAD狀態),
// 則重新置為空閒狀態,結束回收測試
if (state == PooledObjectState.EVICTION) {
state = PooledObjectState.IDLE;
return true;
}
//如果物件是PooledObjectState.EVICTION_RETURN_TO_HEAD狀態(此狀態是由於borrow該物件時發現該對正在進行回收校驗,
// 於是將狀態改為了EVICTION_RETURN_TO_HEAD,表明該物件回收測試還沒完結,但是此時已經被借出),
// 直接將物件置為空閒狀態,返回false表明回該物件在回收期間被借出過(實際上已經完畢在之後已經將物件重新新增到佇列,
// 就目前版本來說返回false與返回true並不代表結束測試不通過,未做任何實現)
else if (state == PooledObjectState.EVICTION_RETURN_TO_HEAD) {
state = PooledObjectState.IDLE;
//將該物件重新插入隊首
if (!idleQueue.offerFirst(this)) {
// TODO - Should never happen
}
}
return false;
}
@Override
public synchronized boolean allocate() {
//分配該物件,在呼叫Pool的borrowObject時會呼叫該方法更改物件狀態為使用狀態,
// 當返回true時才能借用成功
if (state == PooledObjectState.IDLE) {
state = PooledObjectState.ALLOCATED;
lastBorrowTime = System.currentTimeMillis();
lastUseTime = lastBorrowTime;
//同步方法++總借用次數
borrowedCount++;
//如果設定了logAbandoned,則記錄當前的呼叫堆疊資訊
if (logAbandoned) {
borrowedBy.fillInStackTrace();
}
return true;
} else if (state == PooledObjectState.EVICTION) {
//如果該物件是被回收測試狀態則更改狀態為EVICTION_RETURN_TO_HEAD,
// 表明物件正在進行回收測試,並且被借出,當結束回收測試後應該重新將物件新增到佇列頭部
state = PooledObjectState.EVICTION_RETURN_TO_HEAD;
return false;
}
// TODO if validating and testOnBorrow == true then pre-allocate for
// performance
return false;
}
/**
* 歸還時呼叫該方法
*
* @return
*/
@Override
public synchronized boolean deallocate() {
//如果狀態是已分配ALLOCATED或者歸還中RETURNING,則將對想置為空閒狀態IDLE
if (state == PooledObjectState.ALLOCATED ||
state == PooledObjectState.RETURNING) {
state = PooledObjectState.IDLE;
lastReturnTime = System.currentTimeMillis();
borrowedBy.clear();
return true;
}
return false;
}
/**
* 呼叫destory方法是呼叫物件的該方法更改物件狀態為無效狀態INVALID
*/
@Override
public synchronized void invalidate() {
state = PooledObjectState.INVALID;
}
@Override
public void use() {
lastUseTime = System.currentTimeMillis();
usedBy.fillInStackTrace();
}
/**
* 列印堆疊資訊
*
* @param writer
*/
@Override
public void printStackTrace(final PrintWriter writer) {
boolean written = borrowedBy.printStackTrace(writer);
written |= usedBy.printStackTrace(writer);
if (written) {
writer.flush();
}
}
/**
* 返回狀態
*
* @return
*/
@Override
public synchronized PooledObjectState getState() {
return state;
}
/**
* 標記該物件是洩漏的(即超過最大活躍時間)
*/
@Override
public synchronized void markAbandoned() {
state = PooledObjectState.ABANDONED;
}
/**
* borrow方法先呼叫該方法將物件標記為RETURNING,當該物件後續操作執行完後呼叫deallocate將物件置為空閒狀態
*/
@Override
public synchronized void markReturning() {
state = PooledObjectState.RETURNING;
}
@Override
public void setLogAbandoned(final boolean logAbandoned) {
this.logAbandoned = logAbandoned;
}
/**
* Configures the stack trace generation strategy based on whether or not fully
* detailed stack traces are required. When set to false, abandoned logs may
* only include caller class information rather than method names, line numbers,
* and other normal metadata available in a full stack trace.
*
* @param requireFullStackTrace the new configuration setting for abandoned object
* logging
* @since 2.5
*/
// TODO: uncomment below in 3.0
// @Override
public void setRequireFullStackTrace(final boolean requireFullStackTrace) {
borrowedBy = CallStackUtils.newCallStack("'Pooled object created' " +
"yyyy-MM-dd HH:mm:ss Z 'by the following code has not been returned to the pool:'",
true, requireFullStackTrace);
usedBy = CallStackUtils.newCallStack("The last code to use this object was:",
false, requireFullStackTrace);
}
}
以上就是PooledObject<T> 介面的實現原理,大致可以歸納為:
- 實際物件的一個包裝器
- 繼承了Comparable,可以對其進行排序
- 定義了更改物件狀態的一些方法
- 記錄了物件的一些元資料和呼叫堆疊