commons-pool2源碼走讀(一) 池對象PooledObject接口及其實現

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> 接口的實現原理,大致可以歸納爲:

  1. 實際對象的一個包裝器
  2. 繼承了Comparable,可以對其進行排序
  3. 定義了更改對象狀態的一些方法
  4. 記錄了對象的一些元數據和調用堆棧
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章