Apache commons-pool2-2.4.2源码学习笔记

1.背景


       最近查看了下Apache commons-pool2的源代码commons-pool2-2.4.2,代码不多,大概50个java类左右,阅读源代码的初衷是为了通过不断的学习和总结,升华自己的技术能力,写此博客是为了给自己留下一点笔记,日后能够温故知新。

       Apache commons-pool2类库是对象池技术的一种具体实现,它的出现是为了解决频繁的创建和销毁对象带来的性能损耗问题,原理就是建立一个对象池,池中预先生成了一些对象,需要对象的时候进行租借,用完后进行归还,对象不够时灵活的自动创建,对象池满后提供参数控制是否阻塞还是非阻塞响应租借.

       熟悉了Apache commons-pool2对于了解数据库连接池DBCP和了解redis客户端jedis的连接池都有很大帮助,因为jedis的连接池就是基于Apache commons-pool2实现,而DBCP是基于Jakarta commons-pool实现。

相关API

GenericObjectPool继承BaseGenericObjectPool实现ObjectPool,通常用此实现来作为默认的连接池对象。 
BaseGenericObjectPool一个抽象类,主要实现了两个功能:

注册JMX
管理一个连接池驱逐线程,此线程调用GenericObjectPool的evict()驱逐超过最大生命周期的连接对象。
ObjectPool连接池的最上层接口,定义了一个连接池必须具备的方法,比如借用一个连接对象T borrowObject(),归还一个使用后的连接对象void returnObject(T obj)。

BaseGenericObjectPool做了这么几件事:详见:https://blog.csdn.net/qq447995687/article/details/80444627

  1. 默认属性配置
  2. 对象池的计数信息
  3. 一个回收(驱逐)线程
  4. 实现了空闲对象的迭代器
  5. 注册jmx
  6. 定义IdentityWrapper作为保存所有对象的map容器的key值

2.Apache commons-pool2中3类重要接口

  •  PooledObjectFactory/KeyedPooledObjectFactory:是两个接口,作用都是产生PooledObject的工厂,定义了如何makeObject创建、destroyObject销毁、validateObject校验、activateObject激活PooledObject对象,使用Apache commons-pool2的使用者需要自己实现这个接口
  • PooledObject:是一个接口,定义了getCreateTime获取PooledObject创建时间,getActiveTimeMillis获取PooledObject处于激活状态的时间,getIdleTimeMillis获取PooledObject空闲时间,getLastBorrowTime获取PooledObject最近借出时间,getLastReturnTime获取PooledObject最近归还时间,getLastUsedTime获取PooledObject最近使用时间。目前Apache commons-pool2提供了2个默认实现DefaultPooledObject和PooledSoftReference(继承DefaultPooledObject),一般使用DefaultPooledObject即可
  • ObjectPool/KeyedObjectPool:是两个接口,作用都是管理池里面的PooledObject,borrowObject借出PooledObject,returnObject归还PooledObject,invalidateObject调用PooledObjectFactory销毁PooledObject,addObject调用PooledObjectFactory创建PooledObject,getNumIdle给出PooledObject空闲个数,getNumActive给出PooledObject激活的个数,使用Apache commons-pool2的使用者可以使用默认的5个实现(SoftReferenceObjectPool GenericObjectPool ProxiedObjectPool GenericKeyedObjectPool ProxiedKeyedObjectPool),也可以自己实现


       其中PooledObjectFactory PooledObject ObjectPool关系和作用如下图: 

      

  • 其中KeyedPooledObjectFactory PooledObject KeyedObjectPool关系和作用如下图: 

 

3.PooledObject接口类继承关系OOM图


       PooledObject:是一个接口,定义了getCreateTime获取PooledObject创建时间,getActiveTimeMillis获取PooledObject处于激活状态的时间,getIdleTimeMillis获取PooledObject空闲时间,getLastBorrowTime获取PooledObject最近借出时间,getLastReturnTime获取PooledObject最近归还时间,getLastUsedTime获取PooledObject最近使用时间。目前Apache commons-pool2提供了2个默认实现DefaultPooledObject和PooledSoftReference,一般使用DefaultPooledObject即可 


  

该接口源码如下:

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();

}

 3.1.默认实现DefaultPooledObject,用该类能满足绝大多数需求。

看看源码的具体实现:

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);
    }

}

 

  1. 实际对象的一个包装器
  2. 继承了Comparable,可以对其进行排序
  3. 定义了更改对象状态的一些方法
  4. 记录了对象的一些元数据和调用堆栈

 

4.对象池接口ObjectPool和KeyedObjectPool类关系


       Apache commons-pool2里有针对对象池的5个具体实现类SoftReferenceObjectPool GenericObjectPool ProxiedObjectPool GenericKeyedObjectPool ProxiedKeyedObjectPool ,这5个对象池按照实现的接口不同分为2类,一个是实现了接口ObjectPool另一个是实现了接口KeyedObjectPool

    4.1 PowerDesigner反向工程ObjectPool的OOM关系图

  • GenericObjectPool:可配置LIFO/FIFO行为的ObjectPool的实现。默认采用LIFO队列方式。这意味着当有闲置的可用对象在对象池中时,borrowObject方法会返回最近的实例。如果配置文件中的LIFO配置项的值为false,则将返回相反排序的实例,也就是会返回最先进入对象池的对象的实例;
  • SoftReferenceObjectPool:使用LIFO行为实现的ObjectPool。此外,在这个对象池实现中,每个对象都会被包装到一个SoftReference中。SoftReference允许垃圾回收机制在需要释放内存时回收对象池中的对象;
  • ProxiedObjectPool:使用CGLIB或者JDK自带动态代理技术,代理由GenericObjectPool或者SoftReferenceObjectPool产生的ObjectPool对象

    4.2 PowerDesigner反向工程KeyedObjectPool的OOM关系图

  • GenericKeyedObjectPool:可配置LIFO/FIFO行为的ObjectPool的实现。默认采用LIFO队列方式。这意味着当有闲置的可用对象在对象池中时,borrowObject方法会返回最近的实例。如果配置文件中的LIFO配置项的值为false,则将返回相反排序的实例,也就是会返回最先进入对象池的对象的实例;
  • ProxiedKeyedObjectPool:使用CGLIB或者JDK自带动态代理技术,代理由GenericKeyedObjectPool产生的ObjectPool对象

5. PooledObjectState类枚举的PooledObject状态及其转换关系

在上面的接口中,,大多数的方法都用来更改对象的状态,根据对象的状态可以推断出该对象是否正在使用,以及接下来可以进行什么样的操作。对象状态如下:

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
}

 

6.GenericObjectPool的BorrowObject和ReturnObject流程分析


       这里先来一张GenericObjectPool的组图,如下: 

 

        6.1 GenericObjectPool,BorrowObject租借池化对象  

   叙述:

1: 如果 blockWhenExhausted 配置的 为 false,从资源池中获取资源,如果获取不到,则判断当前池中的对象数量是否超过了 maxTotal 设置的数量,如果没有超过, 

则通过调用factory.makeObject() 创建对象,并将对象放入池中,执行第2步 。如果超过了,则返回 null,逻辑到此结束。 

如果 blockWhenExhausted 配置的 为 true ,从资源池中获取资源,如果获取不到,则判断当前池中的对象数量是否超过了 maxTotal 设置的数量,如果没有超过, 

则通过调用factory.makeObject() 创建对象,并将对象放入池中,执行第2步 。如果超过了,则阻塞等待,如果 MaxWaitMillis 配置的为 -1 则 阻塞等待,直到有可用的资源为止。 

如果 maxWaitMillis 配置为 1000 则 阻塞等待 1000毫秒,如果有可用资源,执行第2步,如果没有则返回 null,逻辑到此结束。 

2:将资源的状态 修改为 已分配,执行 第 3 步 

3:调用 factory.activateObject() 方法,执行 第 4步 

4:如果 testOnBorrow 或者 testOnCreate 中有一个 配置 为 true 时,则调用 factory.validateObject() 方法 

5:以上步骤都完成了,返回 资源对象

        6.2 GenericObjectPool.ReturnObject归还池化对象  

叙述: 将资源返还给池的逻辑 


1:检查配置参数 testOnReturn,如果 为 true,调用 factory.validateObject()方法,验证资源对象的有效性,验证结果为 false,则调用 factory.destroyObject()方法,逻辑到此结束。 
验证结果为 true,则执行第 2 步。 
2:调用 factory.passivateObject()方法,然后执行 第 3 步 
3:将资源的状态 修改为 未分配,执行第 4 步 
4:进行判断 ( 资源池是否关闭 || (maxIdle > -1 ) && ( maxIdle <= 资源池空闲资源个数) ) 
如果 判断为 true,则调用 factory.destroyObject()方法,逻辑到此结束。 
如果 判断为 false,则 将资源返还给资源池,逻辑到此结束。


Apache_common_pool 启动一个线程执行释放资源的工作(使用 java.util.Timer 实现)从池中回收资源逻辑(回收资源的意思是,将资源从池中删除掉,例如,如果是TCP链接,则需要将链接断开,并从池中删除掉。) 
1:根据 evictionPolicyClassName 配置的参数创建回收策略, 
默认回收策略源码

public class DefaultEvictionPolicy<T> implements EvictionPolicy<T> {

    @Override
    public boolean evict(EvictionConfig config, PooledObject<T> underTest,
            int idleCount) {

        if ((config.getIdleSoftEvictTime() < underTest.getIdleTimeMillis() &&
                config.getMinIdle() < idleCount) ||
                config.getIdleEvictTime() < underTest.getIdleTimeMillis()) {
            return true;
        }
        return false;
    }
}

 if条件判断与 
(softMinEvictableIdleTimeMillis < 资源的空闲时间 && Math.min(maxIdle,minIdle) < 目前池中空闲的资源数) || 
minEvictableIdleTimeMillis < 资源的空闲时间 
等价 
2:根据配置的参数 numTestsPerEvictionRun 计算,要回收的资源数量(具体的计算规则,请参照源码) 
3:根据回收策略判断,资源是否需要回收。如果 是 则将资源从池中删除,并调用factory.destroyObject()方法。 
如果 否 则根据配置的 testWhileIdle 参数,判断 是否执行 factory.activateObject()和factory.validateObject() ,factory.passivateObject() 方法。如果 testWhileIdle 配置为 true,则以次执行 factory.activateObject(),factory.validateObject(),factory.passivateObject()


7.GenericObjectPoolConfig配置属性介绍
       GenericObjectPoolConfig是一个配置类,提供了很多参数来控制对象池的相关属性,如果你使用过jedis,对于其JedisPoolConfig可能不陌生

  <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="2048" />
        <property name="maxIdle" value="200" />
        <property name="numTestsPerEvictionRun" value="1024" />
        <property name="timeBetweenEvictionRunsMillis" value="30000" />
        <property name="minEvictableIdleTimeMillis" value="-1" />
        <property name="softMinEvictableIdleTimeMillis" value="10000" />
        <property name="maxWaitMillis" value="1500" />
        <property name="testOnBorrow" value="true" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnReturn" value="false" />
        <property name="jmxEnabled" value="true" />
        <property name="blockWhenExhausted" value="false" />
    </bean>

 

而JedisPoolConfig其实就是继承自GenericObjectPoolConfig,GenericObjectPoolConfig的相关配置属性列表如下:
属性类型默认值描述
maxTotalint8池中最多可用的实例个数
maxIdleint8池中最多可容纳的实例(instances)个数
minIdleint0池中最少需要容纳的实例(instances)个数
lifobooleanTRUE资源的存取数据结构,默认值 true,true 资源按照栈结构存取,false 资源按照队列结构存取
fairnessbooleanFALSE当从池中获取资源或者将资源还回池中时 是否使用 java.util.concurrent.locks.ReentrantLock.ReentrantLock 的公平锁机制。 默认值 false, true 使用公平锁,false 不使用公平锁,
maxWaitMillislong-1获取资源时的等待时间,单位毫秒。当 blockWhenExhausted 配置为 true 时,此值有效。 -1 代表无时间限制,一直阻塞直到有可用的资源。
minEvictableIdleTimeMillislong1800000毫秒池中对象处于空闲状态开始到被回收的最短时间
softMinEvictableIdleTimeMillislong1800000毫秒池中对象处于空闲状态开始到被回收的最短时间
numTestsPerEvictionRunint3资源回收线程执行一次回收操作,回收资源的数量。默认值 3, (int类型)。 
备注: 
当 设置为0时,不回收资源。 
设置为 小于0时,回收资源的个数为 (int)Math.ceil( 池中空闲资源个数 / Math.abs(numTestsPerEvictionRun) ); 
设置为 大于0时,回收资源的个数为 Math.min( numTestsPerEvictionRun,池中空闲的资源个数 );
 
evictionPolicyClassNameStringorg.apache.commons.pool2.
impl.DefaultEvictionPolicy
回收策略
testOnCreatebooleanFALSE调用borrowObject方法时,依据此标识判断是否
需要对返回的结果进行校验,如果校验失败会删
除当前实例,并尝试再次获取
testOnBorrowbooleanFALSE默认值 false ,当设置为true时,每次从池中获取资源时都会调用 factory.validateObject() 方法 
testOnReturnbooleanFALSE调用returnObject方法时,依据此标识判断是否
需要对返回的结果进行校验
testWhileIdlebooleanFALSE闲置实例校验标识,如果校验失败会删除当前实例
timeBetweenEvictionRunsMillislong-1 回收资源线程的执行周期,单位毫秒。默认值 -1 ,-1 表示不启用线程回收资源
blockWhenExhaustedbooleantrue当池中对象都被借出后,客户端来租借对象,
此时是否进行阻塞还是非阻塞,默认阻塞
jmxEnabledbooleantrue开启JMX开关
jmxNamePrefixStringpoolJMX前缀
jmxNameBaseStringnullJMX根名字


网友遇到的问题1:mysql服务端设置了连接8小时失效,但是commons-pool2对应的对象池中没有配置上timeBetweenEvictionRunsMillis minEvictableIdleTimeMillis numTestsPerEvictionRun,导致没有对池化的mysql客户端进行检测,所以经验是服务器端如果设置了idel>0的空闲时间, 那么客户端最好设置上对应的心跳频率即多久心跳一次;
网友遇到的问题2:redis的服务端设置了timeout=0,由于网络原因,commons-pool2已经将池中redis客户端销毁,但是服务端redis因为配置了timeout=0禁用了关闭限制的redis客户端功能,导致服务端大量僵尸进程存在,所以经验是配置redis服务端的timeout为一个大于0的值,意思是客户端如果空闲了且空闲时间大于该值,服务端就会关闭该连接
 

8.Apache commons-pool2使用的基本步骤

 

  • 步骤一:实现自己的PooledObjectFactory
  • 步骤二:创建ObjectPool对象
  • 步骤三:从ObjectPool获取到PooledObject对象,进行相关业务操作 


    8.1 实现自己的PooledObjectFactory


       因为Apache commons-pool2不知道开发者需要被池化的对象,所以没有提供默认的相关实现,开发者这一步必须做,这里自己实现Apache commons-pool2的PooledObjectFactory管理StringBuffer为例讲解,其代码如下: 

import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;
/**
 * 
 * @ClassName: MyPooledObjectFactoryExample 
 * @Description: 自己实现Apache commons-pool2的PooledObjectFactory管理StringBuffer
 * @author aperise
 * @date 2017年11月15日 下午9:22:11
 */
public class MyPooledObjectFactoryExample implements PooledObjectFactory<StringBuffer> {
	/**
	 * //创建StringBuffer对象 
	 */
	@Override
	public PooledObject<StringBuffer> makeObject() throws Exception {
		return new DefaultPooledObject<StringBuffer>(new StringBuffer());
	}
	/**
	 * //销毁StringBuffer对象 
	 */
	@Override
	public void destroyObject(PooledObject<StringBuffer> p) throws Exception {
		StringBuffer sb = p.getObject();
		sb = null;
	}
	/**
	 * //校验StringBuffer对象 
	 */
	@Override
	public boolean validateObject(PooledObject<StringBuffer> p) {
		return p.getObject() != null;
	}
	/**
	 * //激活StringBuffer对象 
	 */
	@Override
	public void activateObject(PooledObject<StringBuffer> p) throws Exception {
		if (null == p.getObject())
			p = new DefaultPooledObject<StringBuffer>(new StringBuffer());
	}
	/**
	 * //对话StringBuffer对象,这里是个空实现/
     *   钝化一个对象,在向对象池归还一个对象是会调用这个方法
	 */
	@Override
	public void passivateObject(PooledObject<StringBuffer> p) throws Exception {
		// TODO Auto-generated method stub
 
	}
}
  

 

    8.2 创建ObjectPool对象


       Apache commons-pool2默认提供了对于ObjectPool的5种实现SoftReferenceObjectPool GenericObjectPool ProxiedObjectPool GenericKeyedObjectPool ProxiedKeyedObjectPool,开发者一般不需要自己实现,直接选择使用其中一个即可,如果不用默认的实现也可以自己去实现。 

  //使用Apache commons-pool2的ObjectPool的默认实现GenericObjectPool
        ObjectPool op = new GenericObjectPool<StringBuffer>(new MyPooledObjectFactoryExample(),new GenericObjectPoolConfig());

 

    8.3 从ObjectPool获取到PooledObject对象,进行相关业务操作 

import org.apache.commons.pool2.ObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
 
/**
 * 
 * @ClassName: CommonsPool2Test 
 * @Description: 自己实现Apache commons-pool2的PooledObjectFactory管理StringBuffer
 * @author aperise
 * @date 2017年11月15日 下午9:28:28
 */
public class CommonsPool2Test {
	public static void main(String[] args) {
		//使用Apache commons-pool2的ObjectPool的默认实现GenericObjectPool
		ObjectPool op = new GenericObjectPool<StringBuffer>(new MyPooledObjectFactoryExample(),new GenericObjectPoolConfig());
		
		//从ObjectPool租借对象StringBuffer
		StringBuffer sb = (StringBuffer) op.borrowObject();
		sb.append("aaa");
		System.out.println(sb.toString());
		//归还对象StringBuffer
		op.returnObject(sb);
	}


参考博客:

1、https://blog.csdn.net/zilong_zilong/article/details/78556281

2、https://blog.csdn.net/qq447995687/article/details/80413227

3、https://blog.csdn.net/liang_love_java/article/details/50510753
 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值