Apache Commons-pool实现对象池(包括带key对象池)

Commons-pool是一个apache开源组织下的众多项目的一个。其被广泛地整合到众多需要对象池功能的项目中。

官网:http://commons.apache.org/proper/commons-pool/

本文是commons-pool的一个简单应用,包括不带key的池和带key的池。带key的池是把key相同的池对象放在一起池里,也就是说有多少个key就有多少个池。

不带key的池是生产完全一致的对象放在池里,但是有时候,单用对池内所有对象一视同仁的对象池,并不能解决的问题。例如,对于一组某些参数设置不同的同类对象――比如一堆指向不同地址的  java.net.URL对象或者一批代表不同语句的java.sql.PreparedStatement对象,用这样的方法池化,就有可能取出不合用 的对象的麻烦。这里对带key的池也做了简单的例子。

Commons-pool将对象池化的工作安排给了三类对象:

1.      PoolableObjectFactory(KeyedPoolableObjectFactory):用于管理被池化对象的产生,激活,挂起,检验和销毁。

2.      ObjectPool(KeyedObjectPool):用于管理要被池化的对象的接触和归还,并通过PoolableObjectFactory完成相应的操作。

3.      ObjectPoolFactory(KeyedObjectPoolFactory):ObjectPool的工厂,里边有createPool()方法,用于大量生成相同类型和设置的池。

1.下载相关jar包以及文档

下载API以及相关jar包,本文用到的是目前最新的1.6版本。

下载源码以及官方例子,可供参考。

2.编写测试例子,新建项目只需要导入commons-pool-1.6.jar一个就可以

1)实体对象BaseObject.java

public class BaseObject {

    //记录从池中取出次数
    private int num;
    private boolean active;

    public BaseObject(){
        active = true;
        System.out.println("new BaseObject!!!!!");
    }
//省略get set

2)管理池里对象的产生,激活,挂起,检验和销毁的工厂类

工厂类里的方法内部可以自己根据业务逻辑去写,例如:

makeObject()方法内部可以查询数据库封装成对象,注意一次只能创建一个池对象。

validateObject()方法内部可以自己验证该对象是否正在使用,比如可以通过对象的状态验证。

这是不带key的工厂类TestPoolableFactory.java

public class TestPoolableFactory implements PoolableObjectFactory {

    //重新初始化实例返回池
    @Override
    public void activateObject(Object arg0) throws Exception {
        ((BaseObject)arg0).setActive(true);
    }

    //销毁被破坏的实例
    @Override
    public void destroyObject(Object arg0) throws Exception {
        arg0 = null;
    }

    //创建一个实例到对象池
    @Override
    public Object makeObject() throws Exception {
        BaseObject bo = new BaseObject();
        return bo;
    }

    //取消初始化实例返回到空闲对象池
    @Override
    public void passivateObject(Object arg0) throws Exception {
        ((BaseObject)arg0).setActive(false);
    }

    //验证该实例是否安全
    @Override
    public boolean validateObject(Object arg0) {
        if(((BaseObject)arg0).isActive())
            return true;
        else
            return false;
    }

}

这是带key的工厂类TestKeyPoolableFactory.java

public class TestKeyPoolableFactory implements KeyedPoolableObjectFactory<String, BaseObject> {

    //重新初始化实例返回池
    @Override
    public void activateObject(String arg0, BaseObject arg1) throws Exception {
        ((BaseObject)arg1).setActive(true);
    }

    //销毁被破坏的实例
    @Override
    public void destroyObject(String arg0, BaseObject arg1) throws Exception {
        arg1 = null;
    }

    //创建一个实例到对象池
    @Override
    public BaseObject makeObject(String arg0) throws Exception {
        //这里从数据库里查询出使用次数最少的配置
        BaseObject bo = new BaseObject();
        bo.setNum(0);
        return bo;
    }

    //取消初始化实例返回到空闲对象池
    @Override
    public void passivateObject(String arg0, BaseObject arg1) throws Exception {
        ((BaseObject)arg1).setActive(false);
    }

    //验证该实例是否安全 true:正在使用
    @Override
    public boolean validateObject(String arg0, BaseObject arg1) {
        //这里可以判断实例状态是否可用
        if(((BaseObject)arg1).isActive())
            return true;
        else
            return false;
    }
}

3)测试main方法

不带key的main方法类PoolTest.java

public class PoolTest {

    public static void main(String[] args) {
        BaseObject bo = null;
        PoolableObjectFactory factory = new TestPoolableFactory();
        GenericObjectPool pool = new GenericObjectPool(factory);
        //这里两种池都可以,区别下文会提到
        //ObjectPool pool = new StackObjectPool(factory);
        try {
            for(int i = 0; i < 5; i++) {
                System.out.println("\n==========="+i+"===========");
                System.out.println("池中处于闲置状态的实例pool.getNumIdle():"+pool.getNumIdle());
                //从池里取一个对象,新创建makeObject或将以前闲置的对象取出来
                bo = (BaseObject)pool.borrowObject();
                System.out.println("bo:"+bo);
                System.out.println("池中所有在用实例数量pool.getNumActive():"+pool.getNumActive());
                if((i%2) == 0) {
                    //用完之后归还对象
                    pool.returnObject(bo);
                    System.out.println("归还对象!!!!");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(bo != null) {
                    pool.returnObject(bo);
                }
                //关闭池
                pool.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

输出结果:

===========0===========
池中处于闲置状态的实例pool.getNumIdle():0
new BaseObject!!!!!
bo:[email protected]
池中所有在用实例数量pool.getNumActive():1
归还对象!!!!

===========1===========
池中处于闲置状态的实例pool.getNumIdle():1
bo:[email protected]
池中所有在用实例数量pool.getNumActive():1

===========2===========
池中处于闲置状态的实例pool.getNumIdle():0
new BaseObject!!!!!
bo:[email protected]
池中所有在用实例数量pool.getNumActive():2
归还对象!!!!

===========3===========
池中处于闲置状态的实例pool.getNumIdle():1
bo:[email protected]
池中所有在用实例数量pool.getNumActive():2

===========4===========
池中处于闲置状态的实例pool.getNumIdle():0
new BaseObject!!!!!
bo:[email protected]
池中所有在用实例数量pool.getNumActive():3
归还对象!!!!

这里的池声明用ObjectPool或者GenericObjectPool的区别在于:

ObjectPool这种对象池的特点是:

  • 可以为对象池指定一个初始的参考大小(当空间不够时会自动增长)。
  • 在对象池已空的时候,调用它的borrowObject方法,会自动返回新创建的实例。
  • 可以为对象池指定一个可保存的对象数目的上限。达到这个上限之后,再向池里送回的对象会被自动送去回收。

GenericObjectPool这种对象池的特色是:

  • 可以设定最多能从池中借出多少个对象。
  • 可以设定池中最多能保存多少个对象。
  • 可以设定在池中已无对象可借的情况下,调用它的borrowObject方法时的行为,是等待、创建新的实例还是抛出异常。
  • 可以分别设定对象借出和还回时,是否进行有效性检查。
  • 可以设定是否使用一个单独的线程,对池内对象进行后台清理。
  • ……

这些参数可以通过一个Config类来管理,然后通过GenericObjectPool的setConfig(Config conf)方法来设置自定义的Config类。下面附上setConfig方法源码,就知道Config类需要哪些参数了:

源码GenericKeyedObjectPool.java类内部的setConfig方法

/**
     * Sets the configuration.
     * @param conf the new configuration to use.
     * @see GenericKeyedObjectPool.Config
     */
    public synchronized void setConfig(GenericKeyedObjectPool.Config conf) {
        setMaxIdle(conf.maxIdle);
        setMaxActive(conf.maxActive);
        setMaxTotal(conf.maxTotal);
        setMinIdle(conf.minIdle);
        setMaxWait(conf.maxWait);
        setWhenExhaustedAction(conf.whenExhaustedAction);
        setTestOnBorrow(conf.testOnBorrow);
        setTestOnReturn(conf.testOnReturn);
        setTestWhileIdle(conf.testWhileIdle);
        setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);
        setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);
        setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);
    }

源码GenericObjectPool.java类内部的setConfig方法

/**
     * Sets my configuration.
     *
     * @param conf configuration to use.
     * @see GenericObjectPool.Config
     */
    public void setConfig(GenericObjectPool.Config conf) {
        synchronized (this) {
            setMaxIdle(conf.maxIdle);
            setMinIdle(conf.minIdle);
            setMaxActive(conf.maxActive);
            setMaxWait(conf.maxWait);
            setWhenExhaustedAction(conf.whenExhaustedAction);
            setTestOnBorrow(conf.testOnBorrow);
            setTestOnReturn(conf.testOnReturn);
            setTestWhileIdle(conf.testWhileIdle);
            setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);
            setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);
            setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);
            setSoftMinEvictableIdleTimeMillis(conf.softMinEvictableIdleTimeMillis);
            setLifo(conf.lifo);
        }
        allocate();
    }

1.      参数maxActive指明能从池中借出的对象的最大数目。如果这个值不是正数,表示没有限制。

2.      参数whenExhaustedA ction指定在池中借出对象的数目已达极限的情况下,调用它的borrowObject方法时的行为。可以选用的值有:

  • GenericObjectPool.WHEN_EXHAUSTED_BLOCK,表示等待;
  • GenericObjectPool.WHEN_EXHAUSTED_GROW,表示创建新的实例(不过这就使maxActive参数失去了意义);
  • GenericObjectPool.WHEN_EXHAUSTED_FAIL,表示抛出一个java.util.NoSuchElementException异常。

3.      参数maxWait指明若在对象池空时调用borrowObject方法的行为被设定成等待,最多等待多少毫秒。如果等待时间超过了这个数值,则会抛出一个java.util.NoSuchElementException异常。如果这个值不是正数,表示无限期等待。

4.      参数testOnBorrow设定在借出对象时是否进行有效性检查。

5.      参数testOnBorrow设定在还回对象时是否进行有效性检查。

6.      参数timeBetweenEvictionRunsMillis,设定间隔每过多少毫秒进行一次后台对象清理的行动。如果这个值不是正数,则实际上不会进行后台对象清理。

7.      参数minEvictableIdleTimeMillis,设定在进行后台对象清理时,视休眠时间超过了多少毫秒的对象为过期。过期的对象将被回收。如果这个值不是正数,那么对休眠时间没有特别的约束。

8.      参数testWhileIdle,则设定在进行后台对象清理时,是否还对没有过期的池内对象进行有效性检查。不能通过有效性检查的对象也将被回收。

9.      参数lifo,池对象的放入和取出默认是后进先出的原则,默认是true,代表后进后出,设置为false代表先进先出。

带key的main方法类KeyPoolTest.java

public class KeyPoolTest {

    public static void main(String[] args) {

        BaseObject bo = null;
        BaseObject bo1 = null;
        BaseObject bo2 = null;

        KeyedPoolableObjectFactory<String, BaseObject> keyFactory = new TestKeyPoolableFactory();
        GenericKeyedObjectPool<String, BaseObject> keyPool = new GenericKeyedObjectPool<String, BaseObject>(keyFactory);
        //keyPool.setLifo(false);
        try {
            //这里添加池对象,只需要传入key就会默认调用makeObject()方法创建一个对象
            keyPool.addObject("一级");
            keyPool.addObject("二级");
            //这里注释掉,不初始创建这个键的池对象
            //keyPool.addObject("三级");
            System.out.println("池中处于闲置状态的实例pool.getNumIdle():"+keyPool.getNumIdle());
            for (int i = 0; i < 5; i++) {
                //从池里取对象
                bo = keyPool.borrowObject("一级");
                bo.setNum(bo.getNum()+1);
                System.out.println("一级"+i+"-------"+bo+"-------"+bo.getNum());

                bo1 = keyPool.borrowObject("二级");
                bo1.setNum(bo1.getNum()+1);
                System.out.println("二级"+i+"-------"+bo1+"-------"+bo1.getNum());
                //上边注释掉的那行代码,这里取对象的时候如果没有闲置对象,也会默认去创建一个key="三级"的池对象
                bo2 = keyPool.borrowObject("三级");
                bo2.setNum(bo2.getNum()+1);
                System.out.println("三级"+i+"-------"+bo2+"-------"+bo2.getNum());

                if(i<3) {
                    //用完之后归还对象
                    keyPool.returnObject("一级", bo);
                    keyPool.returnObject("二级", bo1);
                    keyPool.returnObject("三级", bo2);
                    System.out.println("归还对象!!!");
                }
            }
            //当前池里的实例数量
            System.out.println("池中所有在用实例pool.getNumActive():"+keyPool.getNumActive());
            //当前池里的处于闲置状态的实例
            System.out.println("池中处于闲置状态的实例pool.getNumIdle():"+keyPool.getNumIdle());
        } catch (Exception e) {
            e.printStackTrace();
        }
        //这里就不写finally了,偷懒了,这里应该关闭池的
    }
}

输出结果:

new BaseObject!!!!!
new BaseObject!!!!!
池中处于闲置状态的实例pool.getNumIdle():2
一级0[email protected]
二级0[email protected]
new BaseObject!!!!!
三级0[email protected]
归还对象!!!
一级1[email protected]
二级1[email protected]
三级1[email protected]
归还对象!!!
一级2[email protected]
二级2[email protected]
三级2[email protected]
归还对象!!!
一级3[email protected]
二级3[email protected]
三级3[email protected]
new BaseObject!!!!!
一级4[email protected]
new BaseObject!!!!!
二级4[email protected]
new BaseObject!!!!!
三级4[email protected]
池中所有在用实例pool.getNumActive():6
池中处于闲置状态的实例pool.getNumIdle():0

通过输出结果可以看出:

1.对象取出之后可以对对象进行更改,再放回池里这个更改是保留的。

2.池对象的放入和取出默认是后进先出的原则,可以通过池pool的setLifo(boolean lifo)方法设置,默认是true,代表后进先出,设置为false代表先进先出。如果为了池对象使用均衡,推荐使用false。

时间: 2024-08-11 05:29:11

Apache Commons-pool实现对象池(包括带key对象池)的相关文章

Apache Commons pool 简介和pool连接池代码

在实际中工作,我们经常遇到需要连接池的地方,特别是数据库连接池. 我们为什么需要池呢?因为这些资源的创建,都很消耗资源.因此,我们使用一个对象池,里面预先创建了一些资源对象.当我们需要时,从池中取出对象,而不需要时,把对象返回池中.这样就可以提高代码运行的效率. Apache Commons Pool(http://commons.apache.org/pool/)为我们提供了很方便的接口来实现对象池.我们唯一需要实现的就是如何产生对象,而不用去考虑一堆多线程问题. 2013年,Apache C

apache commons pool

apache commons下的pool 其中的borrowObject函数源代码显示其产生可用对象的过程: 如果stack中有空闲的对象,则pop对象,激活对象(activate函数),验证对象(validate函数).最终将合格的对象返回给client. 若对象在这个流程中出错,则在从stack中取出一个,并执行相同的流程.如此循环,直到stack为空. 如果stack为空,则直接调用makeObject函数创建一个对象.在返回对象之前,还会调用验证函数(validate)验证是否有效. 转

Cache Lucene IndexReader with Apache Commons Pool

IndexReaderFactory.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package org.ostree.module.lucene; import org.apache.commons.pool.Ke

The type org.apache.commons.pool.impl.GenericObjectPool$Config cannot be resolved. It is indirectly

static { try { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxActive(MAX_ACTIVE); config.setMaxIdle(MAX_IDLE); config.setMaxWait(MAX_WAIT); config.setTestOnBorrow(TEST_ON_BORROW); jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, A

【Tomcat】【3】报错 Illegal access: this web application instance has been stopped already. Could not load [org.apache.commons.pool.impl.CursorableLinkedList$Cursor]

用Tomcat运行项目报错: Illegal access: this web application instance has been stopped already. Could not load [org.apache.commons.pool.impl.CursorableLinkedList$Cursor]. The following stack trace is thrown for debugging purposes as well as to attempt to term

apache commons JXPath 说明,使用JXPath访问java对象、集合和XML文件

Commons-JXPath 提供了使用Xpath语法操纵符合Java类命名规范的 JavaBeans的工具.也支持 maps, DOM 和其他对象模型.JXPath的主要功能在于一组java类库来使用XPath的方式访问符合JavaBeans规范的java类.java集合(Collections).其他具有动态属性的对象(如Map.ServletContext等),同时提供了一套扩展机制使我们可以增加对这些对象之外的其他对象模型的支持. 如果你要访问的属性不是这个Java类的属性,那么执行过程

Apache Commons Pool2连接池代码

2013年,Apache Commons Pool 2.0 发布,这是一个完全重写的对象池的实现.实现的代码和原来差异很大,原来的一些例子就不能用了.按照上面的例子,用pool2的类和方法重写下. ApacheCommons Pool 2.0 代码如下: package test.ffm83.commons.pool; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.commons.lang.

Apache Commons 工具类介绍及简单使用

Apache Commons包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复劳动.下面是我这几年做开发过程中自己用过的工具类做简单介绍.   组件 功能介绍 BeanUtils 提供了对于JavaBean进行各种操作,克隆对象,属性等等. Betwixt XML与Java对象之间相互转换. Codec 处理常用的编码方法的工具类包 例如DES.SHA1.MD5.Base64等. Collections java集合框架操作. Compress java提供文件打包 压缩类库. C

Apache Commons 工具集介绍

Apache Commons包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复劳动.下面是我这几年做开发过程中自己用过的工具类做简单介绍. 组件 功能介绍 BeanUtils 提供了对于JavaBean进行各种操作,克隆对象,属性等等. Betwixt XML与Java对象之间相互转换. Codec 处理常用的编码方法的工具类包 例如DES.SHA1.MD5.Base64等. Collections java集合框架操作. Compress java提供文件打包 压缩类库. Con