commons-pool2源码走读(四) 对象池实现GenericObjectPool

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

commons-pool2源码走读(四) 对象池实现GenericObjectPool<T>

GenericObjectPool <T> 是一个可配置的ObjectPool实现。
当与适当的PooledObjectFactory组合使用时,GenericObjectPool为任意对象提供健壮的池功能。

您可以选择性的配置池来检查和可能回收池中的空闲对象,并确保有最少数量的空闲对象可用。这是由一个“空闲对象回收”线程(即BaseGenericObjectPool <T> 的Evictor)执行的,线程是异步运行的。在配置这个可选特性时,应该谨慎使用。驱逐运行与客户端线程争用池中的对象,因此如果它们运行得太频繁,可能会导致性能问题。

还可以配置池来检测和删除被泄漏的对象,比如一个从池中借出的对象,在超过removeAbandonedTimeout超时之前既不使用也不返回。移除泄漏的连接,可能发生在对象被借用时对象池已接近饱和,也可能是被回收线程检查出,或者两者都执行时。如果池对象实现了TrackedUse接口,那么其最后一次使用时间使取决于getLastUsed方法;否则,是由对象从池中借出的时间决定。

实现注意:为了防止可能的死锁,已经采取了谨慎措施,以确保在同步块中不会发生对工厂方法的调用。这个类线程安全。

1、接口继承、实现关系

GenericObjectPool <T> 实现了ObjectPool<T> 具备对象池的功能,同时 继承了BaseGenericObjectPool<T> 的对于对象状态管理和回收等功能。
这里写图片描述

2、构造函数

构造函数通过GenericObjectPoolConfig 和PooledObjectFactory来进行参数的初始化和对象工厂类的引入。

    public GenericObjectPool(final PooledObjectFactory<T> factory,
            final GenericObjectPoolConfig config) {
        //父类BaseGenericObjectPool构造方法
        super(config, ONAME_BASE, config.getJmxNamePrefix());

        if (factory == null) {
            jmxUnregister(); // tidy up
            throw new IllegalArgumentException("factory may not be null");
        }
        this.factory = factory;
        //空闲对象队列,此队列非JDK而是自行实现的一个队列
        idleObjects = new LinkedBlockingDeque<>(config.getFairness());
        //覆盖BaseGenericObjectPool里面的配置参数
        setConfig(config);
        //初始化回收线程
        startEvictor(getTimeBetweenEvictionRunsMillis());
    }

3、相关属性

    // --- 可配置的属性 -------------------------------------------------
    //最大空闲数量
    private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;
    //最小空闲数量
    private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;
    //对象工厂
    private final PooledObjectFactory<T> factory;

    // --- 内部属性 -------------------------------------------------

    //池中所有的对象,只能是<=maxActive
    private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
        new ConcurrentHashMap<>();
    //已创建对象总数(不包含已销毁的)
    private final AtomicLong createCount = new AtomicLong(0);
    //调用创建方法总线程数
    private long makeObjectCount = 0;
    //makeObjectCount 增长时并发锁
    private final Object makeObjectCountLock = new Object();
    //空闲对象队列
    private final LinkedBlockingDeque<PooledObject<T>> idleObjects;

    // JMX specific attributes
    private static final String ONAME_BASE =
        "org.apache.commons.pool2:type=GenericObjectPool,name=";

    //泄漏对象回收配置参数
    private volatile AbandonedConfig abandonedConfig = null;

4、 对象池方法实现

  • 借用对象
    整个流程为,检查池是否关闭 –> 是否回收泄漏对象 –> 是否阻塞创建对象 –> 创建对象 –> 分配对象 –> 激活对象 –> 校验对象 –> 更改借用信息 –> 返回对象
   public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
        //判断对象池是否关闭:BaseGenericObjectPool.closed==true
        assertOpen();
        //如果回收泄漏的参数配置不为空,并且removeAbandonedOnBorrow参数配置为true
        //并且Idle数量<2,Active数量>总数Total-3
        //在借用时进行回收泄漏连接(会影响性能)
        final AbandonedConfig ac = this.abandonedConfig;
        if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
                (getNumIdle() < 2) &&
                (getNumActive() > getMaxTotal() - 3) ) {
            //回收泄漏对象
            removeAbandoned(ac);
        }

        PooledObject<T> p = null;

        //copy blockWhenExhausted 防止其它线程更改getBlockWhenExhausted值造成并发问题
        //借用对象时如果没有是否阻塞直到有对象产生
        final boolean blockWhenExhausted = getBlockWhenExhausted();
        //创建成功标识
        boolean create;
        //记录当前时间,用作记录借用操作总共花费的时间
        final long waitTime = System.currentTimeMillis();
        //当对象为空时一直获取
        while (p == null) {
            create = false;
            //从双端队列弹出第一个队首对象,为空返回null
            p = idleObjects.pollFirst();
            //如果为空则重新创建一个对象
            if (p == null) {
                //创建对象
                p = create();
                //p==null可能对象池达到上限不能继续创建!
                if (p != null) {
                    create = true;
                }
            }
            //如果对象p还是为空则阻塞等待
            if (blockWhenExhausted) {
                if (p == null) {
                    if (borrowMaxWaitMillis < 0) {
                        //没有超时时间则阻塞等待到有对象为止
                        p = idleObjects.takeFirst();
                    } else {
                        //有超时时间
                        p = idleObjects.pollFirst(borrowMaxWaitMillis,
                                TimeUnit.MILLISECONDS);
                    }
                }
                //达到超时时间,还未取到对象,则抛出异常
                if (p == null) {
                    throw new NoSuchElementException(
                            "Timeout waiting for idle object");
                }
            } else {
                //未取到对象,则抛出异常
                if (p == null) {
                    throw new NoSuchElementException("Pool exhausted");
                }
            }
            //调用PooledObject.allocate()方法分配对象
            //[具体实现请看](https://blog.csdn.net/qq447995687/article/details/80413227)
            if (!p.allocate()) {
                p = null;
            }
            //分配成功
            if (p != null) {
                try {
                    //激活对象,具体请看factory实现,对象重借出到归还整个流程经历的过程图
                    factory.activateObject(p);
                } catch (final Exception e) {
                    try {
                        destroy(p);
                    } catch (final Exception e1) {
                        // Ignore - activation failure is more important
                    }
                    p = null;
                    if (create) {
                        final NoSuchElementException nsee = new NoSuchElementException(
                                "Unable to activate object");
                        nsee.initCause(e);
                        throw nsee;
                    }
                }
                //对象创建成功,是否进行测试
                if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
                    boolean validate = false;
                    Throwable validationThrowable = null;
                    try {
                        //校验对象,具体请看factory实现,对象重借出到归还整个流程经历的过程图
                        validate = factory.validateObject(p);
                    } catch (final Throwable t) {
                        PoolUtils.checkRethrow(t);
                        validationThrowable = t;
                    }
                    //校验不通过则销毁对象
                    if (!validate) {
                        try {
                            destroy(p);
                            destroyedByBorrowValidationCount.incrementAndGet();
                        } catch (final Exception e) {
                            // Ignore - validation failure is more important
                        }
                        p = null;
                        if (create) {
                            final NoSuchElementException nsee = new NoSuchElementException(
                                    "Unable to validate object");
                            nsee.initCause(validationThrowable);
                            throw nsee;
                        }
                    }
                }
            }
        }
        //更新对象借用状态
        updateStatsBorrow(p, System.currentTimeMillis() - waitTime);

        return p.getObject();
    }

创建对象
当借用时,空闲对象为空,并且未达到池最大数量,则会调用该方法重新创建一个空闲对象

   private PooledObject<T> create() throws Exception {
        int localMaxTotal = getMaxTotal();
        // 如果最大数量为负数则设置为Integer的最大值
        if (localMaxTotal < 0) {
            localMaxTotal = Integer.MAX_VALUE;
        }

        // 是否创建成功的一个flag:
        // - TRUE:  调用工厂类成功创建一个对象
        // - FALSE: 返回空
        // - null:  重复创建
        Boolean create = null;
        while (create == null) {
            synchronized (makeObjectCountLock) {
                //加上本次操作,总共创建个数
                final long newCreateCount = createCount.incrementAndGet();
                if (newCreateCount > localMaxTotal) {
                    //连接池容量已满,不能继续增长。在对最后一个对象的创建上,
                    //加入了设计较为精妙,需细细揣摩
                    createCount.decrementAndGet();
                    //调用创建对象方法线程数=0
                    if (makeObjectCount == 0) {
                        //容量已满并且没有线程调用makeObject()方法,
                        //表明没有任何可能性再继续创建对象,
                        //返回并等待归还的空闲对象
                        create = Boolean.FALSE;
                    } else {
                        //其它线程调用makeObject()方法在创建对象了。
                        //如果继续创建则可能超过对象池容量,不返回false,因为其它线程也在创建,
                        //但是是否能够创建成功是未知的,如果其它线程没能创建成功,
                        //则此线程可能会抢夺到继续创建的权利。
                        //释放锁,等待其它线程创建结束并唤醒该线程
                        makeObjectCountLock.wait();
                    }
                } else {
                    // 对象池未满,从新创建一个对象
                    makeObjectCount++;
                    create = Boolean.TRUE;
                }
            }
        }
        //对象池容量达到上限,返回null重新等待其它线程归还对象
        if (!create.booleanValue()) {
            return null;
        }

        final PooledObject<T> p;
        try {
            //创建一个新对象
            p = factory.makeObject();
        } catch (final Exception e) {
            createCount.decrementAndGet();
            throw e;
        } finally {
            //与上面wait()方法相呼应,
            //如果上面抛出了异常,唤醒其它线程争夺继续创建最后一个资源的权利
            synchronized (makeObjectCountLock) {
                makeObjectCount--;
                makeObjectCountLock.notifyAll();
            }
        }
        //设置泄漏参数,并加入调用堆栈
        final AbandonedConfig ac = this.abandonedConfig;
        if (ac != null && ac.getLogAbandoned()) {
            p.setLogAbandoned(true);
            // TODO: in 3.0, this can use the method defined on PooledObject
            if (p instanceof DefaultPooledObject<?>) {
                ((DefaultPooledObject<T>) p).setRequireFullStackTrace(ac.getRequireFullStackTrace());
            }
        }
        //将创建总数增加,并将对象放入    allObjects  
        createdCount.incrementAndGet();
        allObjects.put(new IdentityWrapper<>(p.getObject()), p);
        return p;
    }

回收泄漏对象

 private void removeAbandoned(final AbandonedConfig ac) {
        // Generate a list of abandoned objects to remove
        final long now = System.currentTimeMillis();
        //超时时间=当前时间-配置的超时时间,如果一个对象的上次借用时间在此时间之前,
        //说明上次借用后经过了removeAbandonedTimeout时间限制还未被归还过即可能是泄漏的对象
        final long timeout =
                now - (ac.getRemoveAbandonedTimeout() * 1000L);
        //泄漏的,需要移除的对象列表
        final ArrayList<PooledObject<T>> remove = new ArrayList<>();
        final Iterator<PooledObject<T>> it = allObjects.values().iterator();
        //遍历池中对象依次判断是否需要移除
        while (it.hasNext()) {
            final PooledObject<T> pooledObject = it.next();
            synchronized (pooledObject) {
                //如果对象的状态为已分配ALLOCATED ,并且已经超过泄漏定义时间则添加到需要移除队列进行统一移除
                if (pooledObject.getState() == PooledObjectState.ALLOCATED &&
                        pooledObject.getLastUsedTime() <= timeout) {
                    pooledObject.markAbandoned();
                    remove.add(pooledObject);
                }
            }
        }

        // 移除泄漏连接,如果配置了打印堆栈,则打印调用堆栈信息
        final Iterator<PooledObject<T>> itr = remove.iterator();
        while (itr.hasNext()) {
            final PooledObject<T> pooledObject = itr.next();
            if (ac.getLogAbandoned()) {
                pooledObject.printStackTrace(ac.getLogWriter());
            }
            try {
                //销毁对象
                invalidateObject(pooledObject.getObject());
            } catch (final Exception e) {
                e.printStackTrace();
            }
        }
    }
  • invalidate对象
    public void invalidateObject(final T obj) throws Exception {
        //从所有对象中取出该对象,如果不存在则抛出异常
        final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
        if (p == null) {
            if (isAbandonedConfig()) {
                return;
            }
            throw new IllegalStateException(
                    "Invalidated object not currently part of this pool");
        }
        //如果对象不是无效状态PooledObjectState.INVALID,则销毁此对象
        synchronized (p) {
            if (p.getState() != PooledObjectState.INVALID) {
                destroy(p);
            }
        }
        //
        ensureIdle(1, false);
    }

销毁对象
销毁对象,并从池中移除,更新对象池已创建数量和总销毁数量

    private void destroy(final PooledObject<T> toDestroy) throws Exception {
        toDestroy.invalidate();
        idleObjects.remove(toDestroy);
        allObjects.remove(new IdentityWrapper<>(toDestroy.getObject()));
        try {
            factory.destroyObject(toDestroy);
        } finally {
            destroyedCount.incrementAndGet();
            createCount.decrementAndGet();
        }
    }

确保最小空闲数量

    private void ensureIdle(final int idleCount, final boolean always) throws Exception {
        //!idleObjects.hasTakeWaiters()如果idleObjects队列还有线程等待获取对象则由最后一个
        //等待者确保最小空闲数量
        if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) {
            return;
        }
        //一直创建空闲对象知道空闲对象数量>总空闲数量阈值
        while (idleObjects.size() < idleCount) {
            final PooledObject<T> p = create();
            if (p == null) {
                // Can't create objects, no reason to think another call to
                // create will work. Give up.
                break;
            }
            //根据先进先出参数,添加对象到队首或者队尾
            if (getLifo()) {
                idleObjects.addFirst(p);
            } else {
                idleObjects.addLast(p);
            }
        }
        //在此过程中如果连接池关闭则clear所有对象
        if (isClosed()) {
            clear();
        }
    }
  • 归还对象

归还对象方法将适用完的对象从新放置回对象池中重复利用。其整个流程为:检查是否存在 –> 检查状态是否正确 –> 是否在归还时测试对象 –> 校验对象 –> 钝化(卸载)对象 –> 结束分配 –> 销毁/归还该对象 –> 更新连接池归还信息

    public void returnObject(final T obj) {
        final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));

        if (p == null) {
            //如果对象为空,并且没有配置泄漏参数则抛出异常,表明该对象不是连接池中的对象
            if (!isAbandonedConfig()) {
                throw new IllegalStateException(
                        "Returned object not currently part of this pool");
            }
            //如果对象为空,表明该对象是abandoned并且已被销毁
            return; 
        }

        synchronized(p) {
            final PooledObjectState state = p.getState();
            //如果被归还的对象不是已分配状态,抛出异常
            if (state != PooledObjectState.ALLOCATED) {
                throw new IllegalStateException(
                        "Object has already been returned to this pool or is invalid");
            }
            //更改状态为returning,避免在此过程中被标记为被遗弃。
            p.markReturning();
        }

        final long activeTime = p.getActiveTimeMillis();
        //是否在归还时测试该对象
        if (getTestOnReturn()) {
            //校验对象
            if (!factory.validateObject(p)) {
                try {
                    //校验不通过则destroy对象
                    destroy(p);
                } catch (final Exception e) {
                    swallowException(e);
                }
                try {
                    //确保最小空闲数量
                    ensureIdle(1, false);
                } catch (final Exception e) {
                    swallowException(e);
                }
                //更新连接池归还信息BaseGenericObjectPool#returnedCount,activeTimes
                updateStatsReturn(activeTime);
                return;
            }
        }
        //校验通过
        try {
            //钝化(卸载)对象
            factory.passivateObject(p);
        } catch (final Exception e1) {
            swallowException(e1);
            try {
                destroy(p);
            } catch (final Exception e) {
                swallowException(e);
            }
            try {
                ensureIdle(1, false);
            } catch (final Exception e) {
                swallowException(e);
            }
            updateStatsReturn(activeTime);
            return;
        }
        //结束分配,如果对象为ALLOCATED或者RETURNING更改对象为空闲IDLE状态
        //具体看org.apache.commons.pool2.impl.DefaultPooledObject#deallocate方法
        if (!p.deallocate()) {
            throw new IllegalStateException(
                    "Object has already been returned to this pool or is invalid");
        }

        final int maxIdleSave = getMaxIdle();
        //如果对象池已经关闭或者空闲数量达到上限,则销毁该对象
        if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
            try {
                destroy(p);
            } catch (final Exception e) {
                swallowException(e);
            }
        } else {
            //否则将归还的对象添加到空闲队列,连接池的最终目的:重用一个连接
            if (getLifo()) {
                idleObjects.addFirst(p);
            } else {
                idleObjects.addLast(p);
            }
            if (isClosed()) {
                // Pool closed while object was being added to idle objects.
                // Make sure the returned object is destroyed rather than left
                // in the idle object pool (which would effectively be a leak)
                clear();
            }
        }
        updateStatsReturn(activeTime);
    }
  • clear连接池
    依次销毁每个链接
    public void clear() {
        PooledObject<T> p = idleObjects.poll();

        while (p != null) {
            try {
                destroy(p);
            } catch (final Exception e) {
                swallowException(e);
            }
            p = idleObjects.poll();
        }
    }
  • 回收对象

此方法实现了org.apache.commons.pool2.impl.BaseGenericObjectPool#evict 方法,用于回收线程回收空闲对象。
回收的整个流程为:判断池是否关闭及是否有空闲对象 –> 根据策略获得回收的条数 –> 判断对象状态开始进行回收 –> 根据回收策略EvictionPolicy判断是否能够回收 –> 如能回收则销毁对象 –> 不能回收则判断是否校验对象 –> 激活对象 –> 校验对象 –> 钝化对象 –> 结束回收更改对象状态 –> 回收泄漏连接

public void evict() throws Exception {
        assertOpen();

        if (idleObjects.size() > 0) {

            PooledObject<T> underTest = null;
            final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();

            synchronized (evictionLock) {
                //回收参数
                final EvictionConfig evictionConfig = new EvictionConfig(
                        getMinEvictableIdleTimeMillis(),
                        getSoftMinEvictableIdleTimeMillis(),
                        getMinIdle());
                //是否在回收时测试对象
                final boolean testWhileIdle = getTestWhileIdle();
                //根据getNumTests()对部分对象进行回收测试
                for (int i = 0, m = getNumTests(); i < m; i++) {
                    //evictionIterator是空闲对象的一个迭代器,可以想象为idleObjects.iterator()
                    if (evictionIterator == null || !evictionIterator.hasNext()) {
                        evictionIterator = new EvictionIterator(idleObjects);
                    }
                    if (!evictionIterator.hasNext()) {
                        // Pool exhausted, nothing to do here
                        return;
                    }
                    //多线程并发时,有可能上面检测到有对象,而另一个对象随后将其借出
                    try {
                        underTest = evictionIterator.next();
                    } catch (final NoSuchElementException nsee) {
                        // 对象被其它线程借出
                        i--;
                        evictionIterator = null;
                        continue;
                    }
                    //根据状态判断是否能够开始回收测试,并更改状态,详细实现请看源码走读(一) 
                    if (!underTest.startEvictionTest()) {
                        // Object was borrowed in another thread
                        // Don't count this as an eviction test so reduce i;
                        i--;
                        continue;
                    }

                    //根据回收策略判断对象是否能够被回收,单独分析
                    boolean evict;
                    try {
                        //根据回收策略判断对象是否能够被回收
                        evict = evictionPolicy.evict(evictionConfig, underTest,
                                idleObjects.size());
                    } catch (final Throwable t) {
                        // Slightly convoluted as SwallowedExceptionListener
                        // uses Exception rather than Throwable
                        PoolUtils.checkRethrow(t);
                        swallowException(new Exception(t));
                        // Don't evict on error conditions
                        evict = false;
                    }
                    //如果能被回收则销毁对象
                    if (evict) {
                        destroy(underTest);
                        destroyedByEvictorCount.incrementAndGet();
                    } else {
                        //不能被回收,则是否进行校验,与借出流程相同,只不过该处只是校验而没有借出实际使用
                        if (testWhileIdle) {
                            boolean active = false;
                            try {
                                //对象已经被借出,直接激活
                                factory.activateObject(underTest);
                                active = true;
                            } catch (final Exception e) {
                                destroy(underTest);
                                destroyedByEvictorCount.incrementAndGet();
                            }
                            if (active) {
                                //激活成功进行校验
                                if (!factory.validateObject(underTest)) {
                                    //校验不通过则销毁对象
                                    destroy(underTest);
                                    destroyedByEvictorCount.incrementAndGet();
                                } else {
                                    try {
                                        //校验通过则重新将对象钝化(卸载)
                                        factory.passivateObject(underTest);
                                    } catch (final Exception e) {
                                        destroy(underTest);
                                        destroyedByEvictorCount.incrementAndGet();
                                    }
                                }
                            }
                        }
                        //结束回收测试,更改对象状态或者添加到空闲队列,
                        //如果在此途中被借出,还需重新添加到idleObjects,具体实现请看源码走读(一)
                        if (!underTest.endEvictionTest(idleObjects)) {
                            // TODO - May need to add code here once additional
                            // states are used
                        }
                    }
                }
            }
        }
        //配置了回收,则进行回收泄漏连接
        final AbandonedConfig ac = this.abandonedConfig;
        if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
            removeAbandoned(ac);
        }
    }

返回有多少对象需要进行回收测试

    private int getNumTests() {
        final int numTestsPerEvictionRun = getNumTestsPerEvictionRun();
        if (numTestsPerEvictionRun >= 0) {
            return Math.min(numTestsPerEvictionRun, idleObjects.size());
        }
        return (int) (Math.ceil(idleObjects.size() /
                Math.abs((double) numTestsPerEvictionRun)));
    }

猜你喜欢

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