commons-pool2源码走读(一) 池对象PooledObject接口及其实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq447995687/article/details/80413227

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. 记录了对象的一些元数据和调用堆栈

猜你喜欢

转载自blog.csdn.net/qq447995687/article/details/80413227
今日推荐