zoukankan      html  css  js  c++  java
  • 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("
    ==========="+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:common.keypool.BaseObject@1fdc96c
    池中所有在用实例数量pool.getNumActive():1
    归还对象!!!!
    
    ===========1===========
    池中处于闲置状态的实例pool.getNumIdle():1
    bo:common.keypool.BaseObject@1fdc96c
    池中所有在用实例数量pool.getNumActive():1
    
    ===========2===========
    池中处于闲置状态的实例pool.getNumIdle():0
    new BaseObject!!!!!
    bo:common.keypool.BaseObject@124bbbf
    池中所有在用实例数量pool.getNumActive():2
    归还对象!!!!
    
    ===========3===========
    池中处于闲置状态的实例pool.getNumIdle():1
    bo:common.keypool.BaseObject@124bbbf
    池中所有在用实例数量pool.getNumActive():2
    
    ===========4===========
    池中处于闲置状态的实例pool.getNumIdle():0
    new BaseObject!!!!!
    bo:common.keypool.BaseObject@a20892
    池中所有在用实例数量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-------common.keypool.BaseObject@158b649-------1
    二级0-------common.keypool.BaseObject@127734f-------1
    new BaseObject!!!!!
    三级0-------common.keypool.BaseObject@1037c71-------1
    归还对象!!!
    一级1-------common.keypool.BaseObject@158b649-------2
    二级1-------common.keypool.BaseObject@127734f-------2
    三级1-------common.keypool.BaseObject@1037c71-------2
    归还对象!!!
    一级2-------common.keypool.BaseObject@158b649-------3
    二级2-------common.keypool.BaseObject@127734f-------3
    三级2-------common.keypool.BaseObject@1037c71-------3
    归还对象!!!
    一级3-------common.keypool.BaseObject@158b649-------4
    二级3-------common.keypool.BaseObject@127734f-------4
    三级3-------common.keypool.BaseObject@1037c71-------4
    new BaseObject!!!!!
    一级4-------common.keypool.BaseObject@1df073d-------1
    new BaseObject!!!!!
    二级4-------common.keypool.BaseObject@1546e25-------1
    new BaseObject!!!!!
    三级4-------common.keypool.BaseObject@b66cc-------1
    池中所有在用实例pool.getNumActive():6
    池中处于闲置状态的实例pool.getNumIdle():0

    通过输出结果可以看出:

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

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

  • 相关阅读:
    Javascript的异步和回调
    JS-使用工厂方法创建对象
    PHPUnit使用教程——PHP环境变量+x-debug+composer+phpunit配置安装(超详细!)
    JQuery 纵向二级菜单与对齐方式
    图像映射<map>、<area>
    打开另一个窗口
    多行文本省略号
    replace 正则
    jquery each用法
    li前面的原点或者方的样式修改html中列表项li所显示的圆点的颜色?,以及相关样式的设定
  • 原文地址:https://www.cnblogs.com/jinzhiming/p/5120670.html
Copyright © 2011-2022 走看看