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

原文地址:http://blog.csdn.net/bhy5683/article/details/8776498

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

[java]  view plain copy
  1. public class BaseObject {  
  2.   
  3.     //记录从池中取出次数  
  4.     private int num;  
  5.     private boolean active;  
  6.       
  7.     public BaseObject(){  
  8.         active = true;  
  9.         System.out.println("new BaseObject!!!!!");  
  10.     }  
  11. //省略get set  

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

工厂类里的方法内部可以自己根据业务逻辑去写,例如:
makeObject()方法内部可以查询数据库封装成对象,注意一次只能创建一个池对象。
validateObject()方法内部可以自己验证该对象是否正在使用,比如可以通过对象的状态验证。
这是不带key的工厂类TestPoolableFactory.java
[java]  view plain copy
  1. public class TestPoolableFactory implements PoolableObjectFactory {  
  2.   
  3.     //重新初始化实例返回池  
  4.     @Override  
  5.     public void activateObject(Object arg0) throws Exception {  
  6.         ((BaseObject)arg0).setActive(true);  
  7.     }  
  8.   
  9.     //销毁被破坏的实例  
  10.     @Override  
  11.     public void destroyObject(Object arg0) throws Exception {  
  12.         arg0 = null;  
  13.     }  
  14.   
  15.     //创建一个实例到对象池  
  16.     @Override  
  17.     public Object makeObject() throws Exception {  
  18.         BaseObject bo = new BaseObject();  
  19.         return bo;  
  20.     }  
  21.   
  22.     //取消初始化实例返回到空闲对象池  
  23.     @Override  
  24.     public void passivateObject(Object arg0) throws Exception {  
  25.         ((BaseObject)arg0).setActive(false);  
  26.     }  
  27.   
  28.     //验证该实例是否安全  
  29.     @Override  
  30.     public boolean validateObject(Object arg0) {  
  31.         if(((BaseObject)arg0).isActive())  
  32.             return true;  
  33.         else  
  34.             return false;  
  35.     }  
  36.   
  37. }  


这是带key的工厂类TestKeyPoolableFactory.java
[java]  view plain copy
  1. public class TestKeyPoolableFactory implements KeyedPoolableObjectFactory<String, BaseObject> {  
  2.   
  3.     //重新初始化实例返回池  
  4.     @Override  
  5.     public void activateObject(String arg0, BaseObject arg1) throws Exception {  
  6.         ((BaseObject)arg1).setActive(true);  
  7.     }  
  8.   
  9.     //销毁被破坏的实例  
  10.     @Override  
  11.     public void destroyObject(String arg0, BaseObject arg1) throws Exception {  
  12.         arg1 = null;  
  13.     }  
  14.   
  15.     //创建一个实例到对象池  
  16.     @Override  
  17.     public BaseObject makeObject(String arg0) throws Exception {  
  18.         //这里从数据库里查询出使用次数最少的配置  
  19.         BaseObject bo = new BaseObject();  
  20.         bo.setNum(0);  
  21.         return bo;  
  22.     }  
  23.   
  24.     //取消初始化实例返回到空闲对象池  
  25.     @Override  
  26.     public void passivateObject(String arg0, BaseObject arg1) throws Exception {  
  27.         ((BaseObject)arg1).setActive(false);  
  28.     }  
  29.   
  30.     //验证该实例是否安全 true:正在使用  
  31.     @Override  
  32.     public boolean validateObject(String arg0, BaseObject arg1) {  
  33.         //这里可以判断实例状态是否可用  
  34.         if(((BaseObject)arg1).isActive())  
  35.             return true;  
  36.         else  
  37.             return false;  
  38.     }  
  39. }  

3)测试main方法

不带key的main方法类PoolTest.java
[java]  view plain copy
  1. public class PoolTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         BaseObject bo = null;  
  5.         PoolableObjectFactory factory = new TestPoolableFactory();  
  6.         GenericObjectPool pool = new GenericObjectPool(factory);  
  7.         //这里两种池都可以,区别下文会提到  
  8.         //ObjectPool pool = new StackObjectPool(factory);  
  9.         try {  
  10.             for(int i = 0; i < 5; i++) {  
  11.                 System.out.println("\n==========="+i+"===========");  
  12.                 System.out.println("池中处于闲置状态的实例pool.getNumIdle():"+pool.getNumIdle());  
  13.                 //从池里取一个对象,新创建makeObject或将以前闲置的对象取出来  
  14.                 bo = (BaseObject)pool.borrowObject();  
  15.                 System.out.println("bo:"+bo);  
  16.                 System.out.println("池中所有在用实例数量pool.getNumActive():"+pool.getNumActive());  
  17.                 if((i%2) == 0) {  
  18.                     //用完之后归还对象  
  19.                     pool.returnObject(bo);  
  20.                     System.out.println("归还对象!!!!");  
  21.                 }  
  22.             }  
  23.         } catch (Exception e) {  
  24.             e.printStackTrace();  
  25.         } finally {  
  26.             try {  
  27.                 if(bo != null) {  
  28.                     pool.returnObject(bo);  
  29.                 }  
  30.                 //关闭池  
  31.                 pool.close();  
  32.             } catch (Exception e) {  
  33.                 e.printStackTrace();  
  34.             }  
  35.         }  
  36.     }  
  37. }  


输出结果:

[plain]  view plain copy
  1. ===========0===========  
  2. 池中处于闲置状态的实例pool.getNumIdle():0  
  3. new BaseObject!!!!!  
  4. bo:common.keypool.BaseObject@1fdc96c  
  5. 池中所有在用实例数量pool.getNumActive():1  
  6. 归还对象!!!!  
  7.   
  8. ===========1===========  
  9. 池中处于闲置状态的实例pool.getNumIdle():1  
  10. bo:common.keypool.BaseObject@1fdc96c  
  11. 池中所有在用实例数量pool.getNumActive():1  
  12.   
  13. ===========2===========  
  14. 池中处于闲置状态的实例pool.getNumIdle():0  
  15. new BaseObject!!!!!  
  16. bo:common.keypool.BaseObject@124bbbf  
  17. 池中所有在用实例数量pool.getNumActive():2  
  18. 归还对象!!!!  
  19.   
  20. ===========3===========  
  21. 池中处于闲置状态的实例pool.getNumIdle():1  
  22. bo:common.keypool.BaseObject@124bbbf  
  23. 池中所有在用实例数量pool.getNumActive():2  
  24.   
  25. ===========4===========  
  26. 池中处于闲置状态的实例pool.getNumIdle():0  
  27. new BaseObject!!!!!  
  28. bo:common.keypool.BaseObject@a20892  
  29. 池中所有在用实例数量pool.getNumActive():3  
  30. 归还对象!!!!  

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

ObjectPool这种对象池的特点是:

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

GenericObjectPool这种对象池的特色是:

  • 可以设定最多能从池中借出多少个对象。
  • 可以设定池中最多能保存多少个对象。
  • 可以设定在池中已无对象可借的情况下,调用它的borrowObject方法时的行为,是等待、创建新的实例还是抛出异常。
  • 可以分别设定对象借出和还回时,是否进行有效性检查。
  • 可以设定是否使用一个单独的线程,对池内对象进行后台清理。
  • ……
这些参数可以通过一个Config类来管理,然后通过 GenericObjectPool的setConfig(Config conf)方法来设置自定义的Config类。下面附上setConfig方法源码,就知道Config类需要哪些参数了:

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

[java]  view plain copy
  1. /** 
  2.      * Sets the configuration. 
  3.      * @param conf the new configuration to use. 
  4.      * @see GenericKeyedObjectPool.Config 
  5.      */  
  6.     public synchronized void setConfig(GenericKeyedObjectPool.Config conf) {  
  7.         setMaxIdle(conf.maxIdle);  
  8.         setMaxActive(conf.maxActive);  
  9.         setMaxTotal(conf.maxTotal);  
  10.         setMinIdle(conf.minIdle);  
  11.         setMaxWait(conf.maxWait);  
  12.         setWhenExhaustedAction(conf.whenExhaustedAction);  
  13.         setTestOnBorrow(conf.testOnBorrow);  
  14.         setTestOnReturn(conf.testOnReturn);  
  15.         setTestWhileIdle(conf.testWhileIdle);  
  16.         setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);  
  17.         setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);  
  18.         setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);  
  19.     }  

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

[java]  view plain copy
  1. /** 
  2.      * Sets my configuration. 
  3.      * 
  4.      * @param conf configuration to use. 
  5.      * @see GenericObjectPool.Config 
  6.      */  
  7.     public void setConfig(GenericObjectPool.Config conf) {  
  8.         synchronized (this) {  
  9.             setMaxIdle(conf.maxIdle);  
  10.             setMinIdle(conf.minIdle);  
  11.             setMaxActive(conf.maxActive);  
  12.             setMaxWait(conf.maxWait);  
  13.             setWhenExhaustedAction(conf.whenExhaustedAction);  
  14.             setTestOnBorrow(conf.testOnBorrow);  
  15.             setTestOnReturn(conf.testOnReturn);  
  16.             setTestWhileIdle(conf.testWhileIdle);  
  17.             setNumTestsPerEvictionRun(conf.numTestsPerEvictionRun);  
  18.             setMinEvictableIdleTimeMillis(conf.minEvictableIdleTimeMillis);  
  19.             setTimeBetweenEvictionRunsMillis(conf.timeBetweenEvictionRunsMillis);  
  20.             setSoftMinEvictableIdleTimeMillis(conf.softMinEvictableIdleTimeMillis);  
  21.             setLifo(conf.lifo);  
  22.         }  
  23.         allocate();  
  24.     }  

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

[java]  view plain copy
  1. public class KeyPoolTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         BaseObject bo = null;  
  6.         BaseObject bo1 = null;  
  7.         BaseObject bo2 = null;  
  8.           
  9.         KeyedPoolableObjectFactory<String, BaseObject> keyFactory = new TestKeyPoolableFactory();  
  10.         GenericKeyedObjectPool<String, BaseObject> keyPool = new GenericKeyedObjectPool<String, BaseObject>(keyFactory);  
  11.         //keyPool.setLifo(false);  
  12.         try {  
  13.             //这里添加池对象,只需要传入key就会默认调用makeObject()方法创建一个对象  
  14.             keyPool.addObject("一级");  
  15.             keyPool.addObject("二级");  
  16.             //这里注释掉,不初始创建这个键的池对象  
  17.             //keyPool.addObject("三级");  
  18.             System.out.println("池中处于闲置状态的实例pool.getNumIdle():"+keyPool.getNumIdle());  
  19.             for (int i = 0; i < 5; i++) {  
  20.                 //从池里取对象  
  21.                 bo = keyPool.borrowObject("一级");  
  22.                 bo.setNum(bo.getNum()+1);  
  23.                 System.out.println("一级"+i+"-------"+bo+"-------"+bo.getNum());  
  24.                   
  25.                 bo1 = keyPool.borrowObject("二级");  
  26.                 bo1.setNum(bo1.getNum()+1);  
  27.                 System.out.println("二级"+i+"-------"+bo1+"-------"+bo1.getNum());  
  28.                 //上边注释掉的那行代码,这里取对象的时候如果没有闲置对象,也会默认去创建一个key="三级"的池对象  
  29.                 bo2 = keyPool.borrowObject("三级");  
  30.                 bo2.setNum(bo2.getNum()+1);  
  31.                 System.out.println("三级"+i+"-------"+bo2+"-------"+bo2.getNum());  
  32.                   
  33.                 if(i<3) {  
  34.                     //用完之后归还对象  
  35.                     keyPool.returnObject("一级", bo);  
  36.                     keyPool.returnObject("二级", bo1);  
  37.                     keyPool.returnObject("三级", bo2);  
  38.                     System.out.println("归还对象!!!");  
  39.                 }  
  40.             }  
  41.             //当前池里的实例数量  
  42.             System.out.println("池中所有在用实例pool.getNumActive():"+keyPool.getNumActive());  
  43.             //当前池里的处于闲置状态的实例  
  44.             System.out.println("池中处于闲置状态的实例pool.getNumIdle():"+keyPool.getNumIdle());  
  45.         } catch (Exception e) {  
  46.             e.printStackTrace();  
  47.         }  
  48.         //这里就不写finally了,偷懒了,这里应该关闭池的  
  49.     }  
  50. }  

输出结果:

[plain]  view plain copy
  1. new BaseObject!!!!!  
  2. new BaseObject!!!!!  
  3. 池中处于闲置状态的实例pool.getNumIdle():2  
  4. 一级0-------common.keypool.BaseObject@158b649-------1  
  5. 二级0-------common.keypool.BaseObject@127734f-------1  
  6. new BaseObject!!!!!  
  7. 三级0-------common.keypool.BaseObject@1037c71-------1  
  8. 归还对象!!!  
  9. 一级1-------common.keypool.BaseObject@158b649-------2  
  10. 二级1-------common.keypool.BaseObject@127734f-------2  
  11. 三级1-------common.keypool.BaseObject@1037c71-------2  
  12. 归还对象!!!  
  13. 一级2-------common.keypool.BaseObject@158b649-------3  
  14. 二级2-------common.keypool.BaseObject@127734f-------3  
  15. 三级2-------common.keypool.BaseObject@1037c71-------3  
  16. 归还对象!!!  
  17. 一级3-------common.keypool.BaseObject@158b649-------4  
  18. 二级3-------common.keypool.BaseObject@127734f-------4  
  19. 三级3-------common.keypool.BaseObject@1037c71-------4  
  20. new BaseObject!!!!!  
  21. 一级4-------common.keypool.BaseObject@1df073d-------1  
  22. new BaseObject!!!!!  
  23. 二级4-------common.keypool.BaseObject@1546e25-------1  
  24. new BaseObject!!!!!  
  25. 三级4-------common.keypool.BaseObject@b66cc-------1  
  26. 池中所有在用实例pool.getNumActive():6  
  27. 池中处于闲置状态的实例pool.getNumIdle():0  

通过输出结果可以看出:

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值