apache-commons-pool-1.5.4 源码解析

前言

Apache-Commons-DBCP是数据库连接池中一款优秀的产品,熟悉dbcp同学都知道,dbcp底层“池”的底层实现,是依赖Commos-Pool完成的,所以,在全面深入的掌握dbcp之前,学习一下commons-pool的我认为是非常必要的。网络上关于这类工具包的教程并不多,因此,我只能从源码入手,来“一探究竟”了。
这里选择的版本为1.5.4,与dbcp-1.4版本中依赖的pool保持一致。

org.apache.commons.pool包

Commons-Pool的包结构很接单,仅有两个包组成,org.apache.commons.pool包内主要是接口和抽象类,org.apache.commons.pool.impl包则是由org.apache.commons.pool包的实现类组成。
org.apache.commons.pool包内有6个接口、4个抽象类和1个工具类组成:

  • 接口
    • KeyedObjectPool
    • KeyedObjectPoolFactory
    • KeyedPoolableObjectFactory
    • ObjectPool
    • ObjectPoolFactory
    • PoolableObjectFactory
  • 抽象类
    • BaseKeyedObjectPool
    • BaseKeyedPoolableObjectFactory
    • BaseObjectPool
    • BasePoolableObjectFactory
  • 工具类
    • PoolUtils

其中最核心的三个接口为:ObjectPool、PoolableObjectFactory和ObjectPoolFactory。

ObjectPool

ObjectPool就是一个对象池,它主要描述一个“池”应该提供的那些功能,主要方法有:

  • void addObject():向池里添加一个对象。
  • Object borrowObject():从池里获取对象。
  • void returnObject(Object obj):将对象交还给池。
  • boolean validateObject(Object obj):校验交还到池中的对象是否合法。

上述三个方法,是“池”这个设计思想的核心。

PoolableObjectFactory

PoolableObjectFactory是对ObjectPool的补充,它的着眼点在池中的“对象”,它提供的主要方法有:

  • Object makeObject():创建一个对象。
  • void destroyObject(Object obj):销毁一个对象

通常情况下,PoolableObjectFactory与ObjectPool是结对出现的。一个ObjectPool必然对应一个PoolableObjectFactory。举两个简单的场景,当对象池初始化时(addObject),此时ObjectPool就会调用PoolableObjectFactory的makeObject方法,创建一个对象,并将对象蓄到池中;当将对象交还给池时(returnObject),ObjectPool会首先调用PoolableObjectFactory的validateObject方法,判断当前对象是否合法,然后根据判断结果选择是否销毁它(调用destroyObject方法)。

ObjectPoolFactory

ObjectPoolFactory就是一个创建对象池的工厂类。
由此,也基本可以梳理出来,commons-pool具体的使用逻辑了。首先,我们需要定义个ObjectPoolFactory对象,由它来创建对象池,对象池其本质就是一个Java对象,所以理论上是可以创建多个。拿到对象池之后,就可以正常使用了,但在这之前要给这个对象池提供一个PoolableObjectFactory,由它来完成具体对象的创建和回收,这么做的目的,是为了保证对象池的“纯净”,也就是说,一个对象池究竟持有哪些对象,在一开始的时候,就是知道的。

其他接口和抽象类

除了上述三个核心接口之外,pool包还提供了另外三个接口:KeyedObjectPool、KeyedPoolableObjectFactory和KeyedObjectPoolFactory。这三个接口在用法上和上送接口完全一致,不一样地方就在于,它们提供了“key”的功能,可以根据key获取指定的对象。
此外,pool包还有4个抽象类,BaseKeyedObjectPool、BaseKeyedPoolableObjectFactory、BaseObjectPool和BasePoolableObjectFactory。光看名字也能看出来,这四个抽象类仅是对那四个接口的简单实现,让那些不方便实现接口的类,可以改为继承。

org.apache.commons.pool.impl包

了解了org.apache.commons.pool包的组成之后,再看org.apache.commons.pool.impl包就会清晰很多,在1.5.4的版本中org.apache.commons.pool.impl包由以下几个类组成:

  • 标准实现
    • GenericKeyedObjectPool
    • GenericKeyedObjectPoolFactory
    • GenericObjectPool
    • GenericObjectPoolFactory
  • 基于堆栈的实现
    • StackKeyedObjectPool
    • StackKeyedObjectPoolFactory
    • StackObjectPool
    • StackObjectPoolFactory
  • 使用软引用的对象池
    • SoftReferenceObjectPool
  • 其他(工具类)
    • CursorableLinkedList
    • CursorableSubList
    • EvictionTimer

在org.apache.commons.pool.impl包并不包含PoolableObjectFactory接口的实现类,也就是说,PoolableObjectFactory接口是需要用户自己去实现的。

GenericObjectPoolFactory

GenericObjectPoolFactory是ObjectPoolFactory接口的标准实现。初始化GenericObjectPoolFactory对象时必须传入一个PoolableObjectFactory对象。同时用户可以自己定义一些参数,这些参数就是构造一个pool对象是用到的属性。关于这些属性,我们放到后面详细介绍。一个GenericObjectPoolFactory对象初始化完成之后,就可以调用它的createPool()方法,得到一个GenericObjectPool对象了。

GenericObjectPool

GenericObjectPool出于严谨的考虑,既继承了BaseObjectPool又实现了ObjectPool。在org.apache.commons.pool.impl包的packageinfo中可以看到这样一段描述:“GenericObjectPool (GenericKeyedObjectPool) provides a more robust (but also more complicated) implementation of ObjectPool (KeyedObjectPool). ”,意思就是GenericObjectPool的实现更为健壮也更为复杂!它是整个commons-pool包的核心和基础!接下来,我将从参数属性、内部类和核心方法实现这几个方面,详细的解析GenericObjectPool类。

内部属性

GenericObjectPool的内部(private)属性、属性的类型和属性的作用,如下表所示:

名称 类型 默认值 作用
_maxIdle int 8 池中最多可容纳的实例(instances)个数
_minIdle int 0 池中最少需要容纳的实例(instances)个数
_maxActive int 8 池中最多可用的实例个数
_maxWait long -1 调用borrowObject方法时,需要等待的最长时间
_whenExhaustedAction byte 1 一组标识位,用来标识当发现池中的可用实例已经用光时,需要做的动作
_testOnBorrow boolean false 调用borrowObject方法时,依据此标识判断是否需要对返回的结果进行校验,如果校验失败会删除当前实例,并尝试再次获取
_testOnReturn boolean false 调用returnObject方法时,依据此标识判断是否需要对返回的结果进行校验
_testWhileIdle boolean false 闲置实例校验标识,如果校验失败会删除当前实例
_timeBetweenEvictionRunsMillis long -1l 闲置实例校验器启动的时间间隔,单位是毫秒
_numTestsPerEvictionRun int 3 闲置实例校验器每次校验实例的个数
_minEvictableIdleTimeMillis long 1800000
_softMinEvictableIdleTimeMillis long -1
_lifo boolean true 池中实例的操作是否按照LIFO(后进先出)的原则
_pool CursorableLinkedList null 用来存放实例对象
_evictionCursor Cursor null 迭代器的一类实现,用来记录那些需要被销毁的对象实例
_factory PoolableObjectFactory null 完成对象操作的工厂类,需要用户自己实现
_numActive int 0 用来记录那些被使用,尚未归还的实例数量
_evictor Evictor null 闲置实例校验器
_numInternalProcessing int 0 用来记录那些处理中的实例
_allocationQueue LinkedList new LinkedList() 用来跟踪所有调用borrowObject方法的线程

除了_testOnBorrow和_testOnReturn这两个属性,其余属性的get/set方法全部是同步的(synchronized)。

内部类

GenericObjectPool内部一共定义了3个内部类:

  • org.apache.commons.pool.impl.GenericObjectPool.Evictor:闲置实例校验(清除)器
    这个内部的类的源码如下:

    /**
     * The idle object evictor {@link TimerTask}.
     * @see GenericObjectPool#setTimeBetweenEvictionRunsMillis
     */
    private class Evictor extends TimerTask {
        /**
         * Run pool maintenance.  Evict objects qualifying for eviction and then
         * invoke {@link GenericObjectPool#ensureMinIdle()}.
         */
        public void run() {
            try {
                evict();
            } catch(Exception e) {
                // ignored
            } catch(OutOfMemoryError oome) {
                // Log problem but give evictor thread a chance to continue in
                // case error is recoverable
                oome.printStackTrace(System.err);
            }
            try {
                ensureMinIdle();
            } catch(Exception e) {
                // ignored
            }
        }
    }

可以看到它是TimerTask的子类,这样我们结合java.util.Timer类就可以做到定时的启动闲置实例校验。当前启动闲置实例校验时,一共只执行了两个方法,先说evict(),这个方法的主要作用是结合类的属性完成闲置实例的校验和清理,校验的逻辑也很简单,程序会首先检查这个实例的空闲时间,空闲时间超过_minEvictableIdleTimeMillis属性的值时就认定需要清理,当_testWhileIdle属性为ture时,程序还会将实例交给PoolableObjectFactory(用户自定义的)再去校验一次,最后如果认定需要清理,则调用PoolableObjectFactory的destroyObject方法,销毁对象。

因为evict()在执行的过程中有可能销毁池中的对象,因此在方法结束后,可能会出现的一种场景就是池中的对象少于minIdle的值,这个时候就需要执行ensureMinIdle()方法,重新完成“蓄池”的动作。

  • org.apache.commons.pool.impl.GenericObjectPool.Config:一个内部静态常量类。
  • org.apache.commons.pool.impl.GenericObjectPool.Latch:Latch直译过来是“门闩”的意思,它其实是对用户“获取对象”操作的一个封装,那究竟封装了哪些信息呢?来看下这个类的两个属性。

        /** object timestamp pair allocated to this latch */
        private ObjectTimestampPair _pair;
        
        /** Wheter or not this latch may create an object instance */
        private boolean _mayCreate = false;

_pair属性是ObjectTimestampPair的对象,ObjectTimestampPair是在KeyedObjectPool的实现类GenericKeyedObjectPool中定义的一个内部类,这个应该是直接复用过来的,这个类也有两个属性,一个是真实存放数据value,另一个是记录对象创建时的时间戳的tstamp。GenericObjectPool对象内部维护的_pool属性中,存放的就是ObjectTimestampPair对象。

        /** Object instance */
        Object value;
        
        /** timestamp */
        long tstamp;
        
        /**
         * Create a new ObjectTimestampPair using the given object and the current system time.
         * @param val object instance
         */
        ObjectTimestampPair(Object val) {
            this(val, System.currentTimeMillis());
        }
        
        ......

_mayCreate的作用可以理解为一个标识,用来标识用户的这次“获取对象”的操作是否需要创建一个新的实例。

核心方法实现

  • addObject()
    在方法的注释上,作者写的很清楚,addObject的作用就是创建一个对象,并将对象“蓄”到池中。一般使用池的时候,都会初始化一些对象在池中,即作者所说的“pre-loading”。方法比较简单,真正干活的,是内部方法addObjectToPool(Object, boolean)。
    /**
     * Create an object, and place it into the pool.
     * addObject() is useful for "pre-loading" a pool with idle objects.
     */
    public void addObject() throws Exception {
        ....
            addObjectToPool(obj, false);
        ....
    }

按照作者的描述addObjectToPool方法做了三件事情:首先校验当前拿到的这个对象是否合法,然后判断是需要将它销毁掉还是放到内部的_pool对象中。

    /**
     * <p>Adds an object to the pool.</p>
     * 
     * <p>Validates the object if testOnReturn == true and passivates it before returning it to the pool.
     * if validation or passivation fails, or maxIdle is set and there is no room in the pool, the instance
     * is destroyed.</p>
     * 
     * <p>Calls {@link #allocate()} on successful completion</p>
     * 
     * @param obj instance to add to the pool
     * @param decrementNumActive whether or not to decrement the active count
     * @throws Exception
     */
    private void addObjectToPool(Object obj, boolean decrementNumActive) throws Exception {
        // 判断object是否合法
        boolean shouldDestroy = !success;
        ....
        // 将对象蓄到池中
         _pool.addFirst(new ObjectTimestampPair(obj));
        // 
        allocate();
        // Destroy the instance if necessary
        if(shouldDestroy) {
            _factory.destroyObject(obj);
        }
    }

在完成“蓄池”后,需要给正在获取对象的现在重新分配资源,及调用allocate方法。

    /**
     * Allocate available instances to latches in the allocation queue.  Then
     * set _mayCreate to true for as many additional latches remaining in queue
     * as _maxActive allows.
     */
    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();//通知被阻塞等待的latch
                }
            } 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;
            }
        }
    }
  • borrowObject()
    方法的功能很简单,就是从池中获取一个对象,但实施起来却比较复杂,尤其在多线程的场景中。来看一下作者在设计这个方法时都考虑了哪些东西。

首先,如果池中有可用的对象,那么根据配置的获取原则(后入先出,或者先入先出),将对象激活并返回,如果设置了testOnBorrow,那么在返回之前还将进行校验,如果校验不通过则销毁当前对象,并继续这个过程,直至从池中找到复合要求的对象或者池中已经没有可用的对象为止。

If there is an idle instance available in the pool, then either the most-recently returned (if lifo == true) or “oldest” (lifo == false) instance sitting idle in the pool will be activated and returned. If activation fails, or testOnBorrow is set to true and validation fails, the instance is destroyed and the next available instance is examined. This continues until either a valid instance is returned or there are no more idle instances available.

如果池中没有可用的对象,接下来的动作要结合maxActive、whenExhaustedAction和maxWait这三个属性的值综合判断。如果当前池中已经被取用的对象小于maxActive的值,则会创建一个新的对象,并将它返回。如果池中的资源已经被耗尽(当前没有可用的对象并且已取用的对象达到maxActive的值),方法会首先阻塞,如果阻塞的时间已经超过了设置(maxWait的值),那么接下来有两种选择,一种选择是抛异常,另一种则是无视maxActive的配置,继续扩充池的容量。

If there are no idle instances available in the pool, behavior depends on the maxActive and (if applicable) whenExhaustedAction and maxWait properties. If the number of instances checked out from the pool is less than maxActive, a new instance is created, activated and (if applicable) validated and returned to the caller.If the pool is exhausted (no available idle instances and no capacity to create new ones), this method will either block (WHEN_EXHAUSTED_BLOCK), throw a NoSuchElementException (WHEN_EXHAUSTED_FAIL), or grow (WHEN_EXHAUSTED_GROW – ignoring maxActive). The length of time that this method will block when whenExhaustedAction == WHEN_EXHAUSTED_BLOCK is determined by the maxWait property.

当池中的资源被耗尽时,所以的线程都会阻塞,都在等待从池中获取可用的资源,1.5之后的commons-pool设计了一种“公平”算法,确保线程们是按照请求的顺序,依次获取对象实例。

When the pool is exhausted, multiple calling threads may be simultaneously blocked waiting for instances to become available. As of pool 1.5, a “fairness” algorithm has been implemented to ensure that threads receive available instances in request arrival order.

上文的描述中,已经基本把整个方法的逻辑交代清楚了,接下来我们就来看一下代码是如何实现的。

    public Object borrowObject() throws Exception {
        // 得到当前时间
        long starttime = System.currentTimeMillis();
        // 新建一个闩锁,用来实现“公平”算法
        Latch latch = new Latch();
        byte whenExhaustedAction;
        long maxWait;
        /*
         * 同步的将闩锁放入队列,为了避免死锁,没有调用属性的get的方法,而是指定赋值。
         * 在高并发的环境下,同时有多个线程请求borrowObject方法,此时,每个线程都会新生成一个Latch对象以及whenExhaustedAction和maxWait,
         * 程序会同步的将Latch对象放入_allocationQueue链表中,然后执行allocate方法(allocate的作用见上文)。
         */
        synchronized (this) {
            // Get local copy of current config. Can't sync when used later as
            // it can result in a deadlock. Has the added advantage that config
            // is consistent for entire method execution
            whenExhaustedAction = _whenExhaustedAction;
            maxWait = _maxWait;

            // Add this request to the queue
            _allocationQueue.add(latch);

            // Work the allocation queue, allocating idle instances and
            // instance creation permits in request arrival order
            allocate();
        }
        // 开始迭代,直到返回对象或抛出异常!for(;;)是死循环的一种写法
        for(;;) {
            synchronized (this) {
                assertOpen();
            }

            // If no object was allocated from the pool above 无法从池中获取对象
            if(latch.getPair() == null) {
                // check if we were allowed to create one 如果池中的对象还没有达到maxActive,标识可以创建一个新的对象
                if(latch.mayCreate()) {
                    // allow new object to be created
                } else {
                    // the pool is exhausted 池中的资源被用光后,依照属性的whenExhaustedAction的值,完成相应的动作,
                    switch(whenExhaustedAction) {
                        // 继续扩充池的容量
                        case WHEN_EXHAUSTED_GROW:
                            // allow new object to be created
                            synchronized (this) {
                                // Make sure another thread didn't allocate us an object
                                // or permit a new object to be created
                                if (latch.getPair() == null && !latch.mayCreate()) {
                                    _allocationQueue.remove(latch); // 这行代码的意思是不在继续为latch分配对象
                                    _numInternalProcessing++;
                                }
                            }
                            break;
                        // 返回失败
                        case WHEN_EXHAUSTED_FAIL:
                            synchronized (this) {
                                // Make sure allocate hasn't already assigned an object
                                // in a different thread or permitted a new object to be created
                                if (latch.getPair() != null || latch.mayCreate()) {
                                    break;
                                }
                                _allocationQueue.remove(latch);
                            }
                            throw new NoSuchElementException("Pool exhausted"); // 直接抛异常
                        // 阻塞等待
                        case WHEN_EXHAUSTED_BLOCK:
                            try {
                                synchronized (latch) {
                                    // Before we wait, make sure another thread didn't allocate us an object
                                    // or permit a new object to be created
                                    if (latch.getPair() == null && !latch.mayCreate()) {
                                        if(maxWait <= 0) {
                                            latch.wait();
                                        } else {
                                            // this code may be executed again after a notify then continue cycle
                                            // so, need to calculate the amount of time to wait
                                            // 计算阻塞时间
                                            final long elapsed = (System.currentTimeMillis() - starttime);
                                            final long waitTime = maxWait - elapsed;
                                            if (waitTime > 0)
                                            {
                                                latch.wait(waitTime);
                                            }
                                        }
                                    } else {
                                        break;
                                    }
                                }
                            } catch(InterruptedException e) {
                                Thread.currentThread().interrupt();
                                throw e;
                            }
                            // 等待超时,抛异常
                            if(maxWait > 0 && ((System.currentTimeMillis() - starttime) >= maxWait)) {
                                synchronized(this) {
                                    // Make sure allocate hasn't already assigned an object
                                    // in a different thread or permitted a new object to be created
                                    if (latch.getPair() == null && !latch.mayCreate()) {
                                        // Remove latch from the allocation queue
                                        _allocationQueue.remove(latch);
                                    } else {
                                        break;
                                    }
                                }
                                throw new NoSuchElementException("Timeout waiting for idle object");
                            } else {
                                continue; // keep looping
                            }
                        default:
                            throw new IllegalArgumentException("WhenExhaustedAction property " + whenExhaustedAction +
                                    " not recognized.");
                    }
                }
            }
            // 开始新生成一个对象
            boolean newlyCreated = false;
            if(null == latch.getPair()) {
                try {
                    Object obj = _factory.makeObject();
                    latch.setPair(new ObjectTimestampPair(obj));
                    newlyCreated = true;
                } finally {
                    if (!newlyCreated) {
                        // object cannot be created
                        synchronized (this) {
                            _numInternalProcessing--;
                            // No need to reset latch - about to throw exception
                            allocate();
                        }
                    }
                }
            }
            // activate & validate the object 激活并返回对象
            try {
                _factory.activateObject(latch.getPair().value);
                if(_testOnBorrow &&
                        !_factory.validateObject(latch.getPair().value)) {
                    throw new Exception("ValidateObject failed");
                }
                synchronized(this) {
                    _numInternalProcessing--;
                    _numActive++;
                }
                return latch.getPair().value;
            }
            // 如果在激活对象的过程中出现异常
            catch (Throwable e) {
                // object cannot be activated or is invalid
                try {
                    _factory.destroyObject(latch.getPair().value); // 销毁这个对象
                } catch (Throwable e2) {
                    // cannot destroy broken object
                }
                synchronized (this) {
                    _numInternalProcessing--; // 修改正在处理中的对象个数
                    if (!newlyCreated) {
                        latch.reset();
                        _allocationQueue.add(0, latch);
                    }
                    allocate(); // 重新分配
                }
                if(newlyCreated) {
                    // 如果是新创建的对象又无法激活,会抛异常
                    throw new NoSuchElementException("Could not create a validated object, cause: " + e.getMessage());
                }
                else {
                    continue; // keep looping 
                }
            }
        }
    }

通过对上述的源码分析可以看到,程序对关键资源的操作都是同步的(synchronized),按照资源类型大致可以分为:

  1. 全局属性配置:_whenExhaustedAction、_maxWait。属于全局配置,当pool初始化时就已经确定。
  2. 全局资源变量:_allocationQueue、_pool和_numInternalProcessing。用作全局控制,操作的结果会影响其他方法。
  3. 方法内部变量:latch。每个调用borrowObject()方法的线程都会生成一个latch对象。

同步的处理操作解决了并发问题,但同时会有性能上的损失,对于一个工具类的项目,我觉得性能只是考虑解决方案的一部分,功能的稳定性才是需要关注的重点!

  • returnObject(Object) 有了解析borrowObject()方法的经验,再看returnObject(Object)方法,就比较简单了。
    /**
     * <p>Returns an object instance to the pool.</p>
     * 
     * <p>If {@link #getMaxIdle() maxIdle} is set to a positive value and the number of idle instances
     * has reached this value, the returning instance is destroyed.</p>
     * 
     * <p>If {@link #getTestOnReturn() testOnReturn} == true, the returning instance is validated before being returned
     * to the idle instance pool.  In this case, if validation fails, the instance is destroyed.</p>
     * 
     * <p><strong>Note: </strong> There is no guard to prevent an object
     * being returned to the pool multiple times. Clients are expected to
     * discard references to returned objects and ensure that an object is not
     * returned to the pool multiple times in sequence (i.e., without being
     * borrowed again between returns). Violating this contract will result in
     * the same object appearing multiple times in the pool and pool counters
     * (numActive, numIdle) returning incorrect values.</p>
     *
     * 注意:一定要避免一个对象被多次“返还”的情况发生!
     * 
     * @param obj instance to return to the pool
     */
    public void returnObject(Object obj) throws Exception {
        try {
            addObjectToPool(obj, true);
        } catch (Exception e) {
            if (_factory != null) {
                try {
                    _factory.destroyObject(obj);
                } catch (Exception e2) {
                    // swallowed
                }
                // TODO: Correctness here depends on control in addObjectToPool.
                // These two methods should be refactored, removing the
                // "behavior flag", decrementNumActive, from addObjectToPool.
                synchronized(this) {
                    _numActive--;
                    allocate();
                }
            }
        }
    }

示例:数据库连接池

讲了这么多,是时候自己动手练练了。这次的例子很简单,就是使用commons-pool构造自己的数据库连接池,既是对commons-pool使用方法的总结,也顺便熟悉熟悉数据库连接池的业务,为马上要开始的commons-dbcp包的源码解析热身。

对象构造工厂

package dao;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import org.apache.commons.pool.PoolableObjectFactory;

/**
 * 依赖Apache Commons Pool实现数据库连接池,定义一个数据连接对象的工厂
 * 
 * @author 许广 @
 * 
 */
public class ConnectionPoolFactory implements PoolableObjectFactory {

    private DataSource dataSource;

    public ConnectionPoolFactory(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public Object makeObject() throws Exception {
        return dataSource.getConnection();
    }

    @Override
    public void destroyObject(Object obj) throws Exception {
        ((Connection) obj).close();
    }

    @Override
    public boolean validateObject(Object obj) {
        try {
            Statement statement = ((Connection) obj).createStatement();
            ResultSet rs = statement.executeQuery(" SELECT 1 FROM DUAL");
            while (rs.next()) {
                return true;
            }
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    @Override
    public void activateObject(Object obj) throws Exception {
        try {
            Statement statement = ((Connection) obj).createStatement();
            statement.execute(" SELECT 1 FROM DUAL");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void passivateObject(Object obj) throws Exception {

    }

}

测试连接池的使用

package dao;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

import org.apache.commons.pool.impl.GenericObjectPool;

import oracle.jdbc.pool.OracleConnectionPoolDataSource;

/**
 * 
 * 数据库连接池测试
 * 
 * @author 许广 @
 * 
 */
public class ConnectionPoolTest {
    public static void main(String arg[]) throws Exception {
        
        System.out.println("初始化数据源");
        OracleConnectionPoolDataSource dataSource = new OracleConnectionPoolDataSource();
        dataSource.setURL("jdbc:oracle:thin:@192.168.22.63:1521:orcl");
        dataSource.setUser("scott");
        dataSource.setPassword("tiger");
        
        System.out.println("创建数据库连接池");
        ConnectionPoolFactory factory = new ConnectionPoolFactory(dataSource);
        GenericObjectPool pool = new GenericObjectPool(factory);
        System.out.println("当前数据库连接池的容量" + pool.getNumActive());
        System.out.println("获取一个数据库连接");
        Connection conn = (Connection)pool.borrowObject();
        System.out.println("当前数据库连接池的容量" + pool.getNumActive());
        System.out.println("开始执行sql脚本");
        Statement statement = conn.createStatement();
        ResultSet rs = statement.executeQuery("SELECT ID FROM EASYPASSPORT_USERS ");
        while (rs.next()) {
            long id = rs.getLong(1);
            System.out.println("从数据库中得到一条记录的值" + id);
        }
        rs.close();
        statement.close();
        System.out.println("将对象返还给连接池");
        pool.returnObject(conn);
    }
}

得到结果

初始化数据源
创建数据库连接池
当前数据库连接池的容量0
获取一个数据库连接
当前数据库连接池的容量1
开始执行sql脚本
从数据库中得到一条记录的值999
将对象返还给连接池

总结

理论上来讲,如果在系统资源有充足的前提下,使用任何创建起来比较占用资源的对象,都应该“池化”。最典型,应用最多的就数据库连接池(包括关系型和非关系型数据库)。一个池应至少应该包含以下两个功能,一是对象的缓存、获取和归还,二是池自身属性的自动维护更新。这两个都属于池的核心功能,实现的质量高低,直接决定池的品质。不同通途的池可在这两个功能的基础上再扩展出一些差异话的属性。
commons-pool给出了一个池的经典实现,而且源码简洁清晰,读起来有一种设计的美感。平时在日常工作中,如果需要对象池,commons-pool是你不二的选择。

参考

关于DBCP及使用,纯JDBC架构

标签

发表评论