Apache commons-pool 技术

 Commons-Pool

首先看下两个重要的类结构:

ObjectPool defines a simple pooling interface.

  • GenericObjectPool: ObjectPool implementation with configurable LIFO/FIFO behavior. The default behavior is for the pool to act as a LIFO queue. What this means is that when there are idle objects available in the pool, borrowObject returns the most recently returned ("last in") instance. If the lifo the property of the pool false, instances are returned in the oppposite order - first-in, first-out.
  • StackObjectPool: ObjectPool implementation with a LIFO (Last In First Out) behavior.
  • SoftReferenceObjectPool: ObjectPool implementation with a LIFO (Last In First Out) behavior. Additionally this pool wraps each object in a SoftReference allowing the garbage collector to remove them in response to memory demand.

ObjectPool 定义了池相关操作的接口

 

GenericObjectPool 提供了后进先出(LIFO)与先进先出(FIFO)两种行为模式的池。默认情况是采用后进先出,即当有空闲对象时,调用borrowObject方法,返回最近时刻放进去的那个实例对象。这个行为是通过lifo这个属性控制的。

 

StackObjectPool 采用后进先出行为模式的池。(栈的特点,学过数据结构的都懂的)

 

SoftReferenceObjectPool 采用后进先出行为模式的池。池内对象被SoftReference包裹,允许垃圾回收器在有内存需要的时候删除这部分对象。

 

除了上述信息外,如果你查看过三个实现类的源码,会发现下面这些特点:

 

GenericObjectPool 采用的是org.apache.commons.collections.CursorableLinkedList对象来保存对象池里的对象。而在新版本中(至少1.5.6已经是了),CursorableLinkedList这里类被拷贝到了org.apache.commons.pool.impl包下, 也就是说是用pool组件不再需要依赖commons-collections组件了。

 

StackObjectPool 采用的是java.util.Stack对象来保存对象池里的对象。

 

SoftReferenceObjectPool 采用的是java.util.ArrayList对象来保存对象池里的对象。关于这里的软引用问题,不做深入解释,大家只要明白当内存不足的时候,池中的对象可以被Java虚拟机回收。

 

GenericObjectPool 是三个实现中最复杂的,每次调用borrowObject或returnObject,度会涉及空闲对象分配,多线程等问题。分析源码时,推荐先从StackObjectPool看起,最后再分析GenericObjectPool。

KeyedObjectPool pools instances of multiple types. Each type may be accessed using an arbitrary key.

 

KeyedObjectPool 包含多种类型的池,采用K-V方式,每个类型都可以通过特定的Key获取。

 

GenericKeyedObjectPool采用先进先出行为模式的池。

 

StackKeyedObjectPool采用后进先出行为模式的池。

KeyedObjectPool的实现类特点可以参照对应的ObjectPool实现类,基本结构是一致的。

 

通过上面的信息,可以看到主要的池接口就是ObjectPool与KeyedObjectPool,从他们作为入口来进行分析。

 

ObjectPool

从图中可以看出来:

 

GenericObjectPool是继承了BaseObjectPool,BaseObjectPool实现了ObjectPool接口,因此GenericObjectPool是间接实现了ObjectPool接口。

 

其中ObjectPool与BaseObjectPool中的setFactory方法在最新的版本2.0中(分析时查看的是svn中的源码)已经被移除,因为基本上在new一个工厂实例的时候就需要放入这个factory,所以这个方法被移除是正常的。

 

并且发现2.0最大的特点就是支持泛型了,也就是说需要JDK1.5以上的支持。

 

org.apache.commons.pool.ObjectPool

 

接口中提供了如下方法:

Object borrowObject() // 从池中获得一个对象   
void returnObject(Object obj) // 返回一个对象给池   
void invalidateObject(Object obj) // 使对象实效,不再受池管辖(必须是已经从池中获得的对象)   
void addObject() // 生成一个对象(通过工程或其他实现方式),并将其放入空闲队列中   
int getNumIdle() // 获得空闲对象的数量   
int getNumActive() // 获得活动对象的数量   
void clear() // 清空池中空闲对象,释放相关资源   
void close() // 关闭池,释放所有与它相关资源   
void setFactory(PoolableObjectFactory factory) // 设置池对象工厂


org.apache.commons.pool.BaseObjectPool

 

这个类没啥可说的,大部分方法都是简单实现或直接抛异常,稍微看下源码就会明白。

 

org.apache.commons.pool.impl.StackObjectPool

 

构造函数中,就是将池容器一个stack对象初始化,并设定最小容量(默认为4)。

 

borrowObject方法

public synchronized Object borrowObject() throws Exception {   
    assertOpen();   
    Object obj = null;   
    boolean newlyCreated = false;   
    while (null == obj) {   
       //  池是否为空   
        if (!_pool.empty()) {   
          // 栈里有空闲对象,弹出栈中最近放进去的那个对象,即栈顶对象   
            obj = _pool.pop();   
        } else {   
          // 池是空的,这里会先判断工厂是否为空   
            if(null == _factory) {   
                throw new NoSuchElementException();   
            } else {   
              // 调用工厂的生产新对象   
                obj = _factory.makeObject();   
                newlyCreated = true;   
              if (obj == null) {   
                throw new NoSuchElementException("PoolableObjectFactory.makeObject() returned null.");   
              }   
            }   
        }   
        // 如果生产了对象,需要先激活对象(使其成为初始状态),再进行验证,验证不通过的话,需要销毁这个对象   
        if (null != _factory && null != obj) {   
            try {   
                _factory.activateObject(obj);   
                if (!_factory.validateObject(obj)) {   
                    throw new Exception("ValidateObject failed");   
                }   
            } catch (Throwable t) {   
                PoolUtils.checkRethrow(t);   
                try {   
                    _factory.destroyObject(obj);   
                } catch (Throwable t2) {   
                    PoolUtils.checkRethrow(t2);   
                    // swallowed   
                } finally {   
                    obj = null;   
                }   
                if (newlyCreated) {   
                    throw new NoSuchElementException(   
                        "Could not create a validated object, cause: " +   
                        t.getMessage());   
                }   
            }   
        }   
    }   
   // 活动对象数加一,返回对象   
    _numActive++;   
    return obj;   
}  


过程非常简单,先判断池是否已经关闭,然后进去借对象的循环,直到接到对象或抛出异常为止。

 

returnObject方法

public synchronized void returnObject(Object obj) throws Exception {   
    // 池是否关闭了   
        boolean success = !isClosed();   
        if(null != _factory) {   
           // 验证对象   
            if(!_factory.validateObject(obj)) {   
                success = false;   
            } else {   
                try {   
                 // 验证通过的情况下,需要将这个对象设置成空闲对象的状态   
                    _factory.passivateObject(obj);   
                } catch(Exception e) {   
                    success = false;   
                }   
            }   
        }   
       // 根据池是否关闭与验证结果,判断这个对象是否应该销毁   
        boolean shouldDestroy = !success;   
       // 活动对象数减一   
        _numActive--;   
        if (success) {   
           // 如果池中空闲对象已经达到了设定的最大值,那么将池中第一个对象弹出并销毁,将刚刚传入的那个对象压入栈中   
            Object toBeDestroyed = null;   
            if(_pool.size() >= _maxSleeping) {   
                shouldDestroy = true;   
                toBeDestroyed = _pool.remove(0); // remove the stalest object   
            }   
            _pool.push(obj);   
            obj = toBeDestroyed; // swap returned obj with the stalest one so it can be destroyed   
        }   
        notifyAll(); // _numActive has changed   
    
       // 进行对象销毁   
        if(shouldDestroy) { // by constructor, shouldDestroy is false when _factory is null   
            try {   
                _factory.destroyObject(obj);   
            } catch(Exception e) {   
                // ignored   
            }   
        }   
    }   


过程依然简单,就是先验证传入的对象,验证通过,就放入池中。

 

addObject方法与returnObject及其类似,就是传入的那个对象,变成了通过工厂方法makeObject来生成,其他过程完全一致,这里不再分析。

其他几个方法invalidateObject,clear等十分简单,也不分析了。

 

总结一下,通过对StackObjectPool的分析,就可以使大家对池的管理有一个总体的认识。

 

池对于对象的管理,主要就是分为借出,归还,验证,销毁,激活,钝化这几个功能。

 

而其中的一些细节就是如何组织这几个功能,比如在借出与归还前都要进行对象的验证,放入池前需要钝化对象等。

 

org.apache.commons.pool.impl.SoftReferenceObjectPool

 

看过StackObjectPool再看SoftReferenceObjectPool,你会发现如此相似,甚至逻辑都一致。

 

最大区别就是放入池中对象是下面这样的:

对象被软引用包装了起来,所以使得在内存不足的时候,可以让垃圾回收器强制销毁掉这部分对象,来释放内存。

 

有个比较奇怪的地方就是,在分析源码的过程中,竟然没有找到这个池对象的工厂类...

亲爱的SoftReferenceObjectPoolFactory你到底是藏到哪里去了…

 

org.apache.commons.pool.impl.GenericObjectPool

 

这个类比较复杂,前三百多行都是定义的一些常量,基本上就是pool的默认配置信息了。

 

紧接着是构造函数,你会看到n多构造函数,主要是参数不同。

 

第一个参数必然是PoolableObjectFactory(几乎所有的Pool对象实现类都要依赖这个工厂类,后面会详细讲解), 后面的参数是相关设置参数,你可以单独设置,或者使用一个内部类PoolableObjectFactory.Config,里面全是都是public的属性,可以直接修改。

 

关于PoolableObjectFactory.Config的属性,采用默认配置是完全可以正常工作的,当然如果你需要更加细致的控制,你就必须了解这些属性

public static class Config {   
        // 允许最大空闲对象数   
        public int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE;   
        // 允许最小空闲对象数   
        public int minIdle = GenericObjectPool.DEFAULT_MIN_IDLE;   
        // 允许最大活动对象数   
        public int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE;   
        // 允许最大等待时间毫秒数   
        public long maxWait = GenericObjectPool.DEFAULT_MAX_WAIT;   
        // 当池中对象用完时,请求新的对象所要执行的动作   
        public byte whenExhaustedAction = GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION;   
        // 是否在从池中取出对象前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个   
        public boolean testOnBorrow = GenericObjectPool.DEFAULT_TEST_ON_BORROW;   
        // 是否在向池中归还对象前进行检验,如果检验失败   
        public boolean testOnReturn = GenericObjectPool.DEFAULT_TEST_ON_RETURN;   
        // 连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除   
        public boolean testWhileIdle = GenericObjectPool.DEFAULT_TEST_WHILE_IDLE;   
        // 在空闲连接回收器线程运行期间休眠的时间毫秒数. 如果设置为非正数,则不运行空闲连接回收器线程   
        public long timeBetweenEvictionRunsMillis = GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;   
        // 设定在进行后台对象清理时,每次检查对象数   
        public int numTestsPerEvictionRun =  GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN;   
        // 被空闲对象回收器回收前在池中保持空闲状态的最小时间毫秒数   
        public long minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;   
        // 被空闲对象回收器回收前在池中保持空闲状态的最小时间毫秒数   
        public long softMinEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS;   
        // 是否采用后进先出策略   
        public boolean lifo = GenericObjectPool.DEFAULT_LIFO;   
    }  


从六百行到一千行这部分,是上面修改参数的方法,其中主要涉及一个allocate方法,会从新分配空闲对象。

 

下面分析下这个方法:

private synchronized void allocate() {   
    if (isClosed()) return;   
  
    // First use any objects in the pool to clear the queue   
    for (;;) {   
        if (!_pool.isEmpty() && !_allocationQueue.isEmpty()) {   
            Latch latch = (Latch) _allocationQueue.removeFirst();   
            latch.setPair((ObjectTimestampPair) _pool.removeFirst());   
            _numInternalProcessing++;   
            synchronized (latch) {   
                latch.notify();   
            }   
        } else {   
            break;   
        }   
    }   
  
    // Second utilise any spare capacity to create new objects   
    for(;;) {   
        if((!_allocationQueue.isEmpty()) && (_maxActive < 0 || (_numActive + _numInternalProcessing) < _maxActive)) {   
            Latch latch = (Latch) _allocationQueue.removeFirst();   
            latch.setMayCreate(true);   
            _numInternalProcessing++;   
            synchronized (latch) {   
                latch.notify();   
            }   
        } else {   
            break;   
        }   
    }   
}   


首先判断该池是否已经关闭

 

然后…

 

再然后…

 

额,暂时没有完全看懂,实在不好乱讲,以后再详细补完这部分。

 

borrowObjcet,returnObject 也稍后补完。

 

 

下面看一下一个比较重要的类,基本上你要使用Pool框架,都要与它打交道

org.apache.commons.pool.PoolableObjectFactory

 

这是一个接口类,定义了生成对象,销毁对象,验证对象,激活对象,钝化对象的方法。

Object makeObject()   
void destroyObject(Object obj)   
boolean validateObject   
void activateObject(Object obj)   
void passivateObject(Object obj)  


当时要使用Commons-Pools时,你就需要新建一个类,实现这个接口,然后实例化你需要的工厂类(StackObjectPoolFactory,GenericObjectPoolFactory,根据你的需要来选择吧),然后用工厂生成你的池对象(Pool)。 

 

Description: An attempt was made to call a method that does not exist. The attempt was made from the following location: org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$PoolBuilderFactory.getPoolConfig(LettuceConnectionConfiguration.java:207) The following method did not exist: 'void org.apache.commons.pool2.impl.GenericObjectPoolConfig.setMaxWait(java.time.Duration)' The calling method's class, org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$PoolBuilderFactory, was loaded from the following location: jar:file:/D:/Developing%20learning%20software/apache-maven-3.9.2-bin/nfv/org/springframework/boot/spring-boot-autoconfigure/3.1.2/spring-boot-autoconfigure-3.1.2.jar!/org/springframework/boot/autoconfigure/data/redis/LettuceConnectionConfiguration$PoolBuilderFactory.class The called method's class, org.apache.commons.pool2.impl.GenericObjectPoolConfig, is available from the following locations: jar:file:/D:/Developing%20learning%20software/apache-maven-3.9.2-bin/nfv/org/apache/commons/commons-pool2/2.6.0/commons-pool2-2.6.0.jar!/org/apache/commons/pool2/impl/GenericObjectPoolConfig.class The called method's class hierarchy was loaded from the following locations: org.apache.commons.pool2.impl.GenericObjectPoolConfig: file:/D:/Developing%20learning%20software/apache-maven-3.9.2-bin/nfv/org/apache/commons/commons-pool2/2.6.0/commons-pool2-2.6.0.jar org.apache.commons.pool2.impl.BaseObjectPoolConfig: file:/D:/Developing%20learning%20software/apache-maven-3.9.2-bin/nfv/org/apache/commons/commons-pool2/2.6.0/commons-pool2-2.6.0.jar org.apache.commons.pool2.BaseObject: file:/D:/Developing%20learning%20software/apache-maven-3.9.2-bin/nfv/org/apache/commons/commons-pool2/2.6.0/commons-pool2-2.6.0.jar Action: Correct the classpath of your application so that it contains compatible versions of the classes org.springframework.boot.autoconfigure.data.redis.LettuceConnectionConfiguration$PoolBuilderFactory and org.apache.commons.pool2.impl.GenericObjectPoolConfig
07-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值