Change Dir

先知cd——热爱生活是一切艺术的开始

统计

留言簿(18)

积分与排名

“牛”们的博客

各个公司技术

我的链接

淘宝技术

阅读排行榜

评论排行榜

commons-pool学习笔记

Object pool就是一个管理对象的池子。新版本利用jdk 1.5以后的特性,结合泛型,而不是利用Object来实现了。

主要就靠3个接口来管理:

ObjectPool, 定义了池接口,就是说,以后的对象池,至少模子是这个样子的~~主要两个实现抽象类:BaseObjectPool和KeyedObjectPool。有一些基本方法比如从对象池取对象,调用borrowObject()方法,将对象放回使用returnObject()方法。清空选择clear,而不需要对象池选择关闭时使用close等。在具体的Pool实现中,会依赖PoolableObjectFactory接口。

PoolableObjectFactory,怎么理解这个接口呢?其实在ObjectPool中我们看到只是声明了一些调用和放回对象的方法,可以理解为真正我们在操作一个pool,这就是一个数据结构。然而pool中的对象创建、管理、销毁等事务pool管不了了。因此框架又定义了这样一个factory去管理这些对象。该接口有makeObject方法,我们自己的factory实现里可以利用这个方法去创建个性化的对象。有创建就有销毁,destroyObject(T obj)方法就用来销毁对象。同时还有activateObject(T obj)方法用来激活对象,也就是说令对象可用。有passivateObject(T obj)方法用来将对象作为idle状态。同时还有validateObject(T obj)方法来检验对象是否可以由pool安全返回。

ObjectPoolFactory,作为最后一个接口,我感觉这个factory应该算是用处不特别大吧。顾名思义,用来管理pool的一个factory。我们的应用如果需要的话,可能不止一个pool。由一个factory去管理,明显合理的,而且factory模式也是pool框架的特色。该接口只有一个方法,那就是createObjectPool()。

 

框架的类图主要关系就是这样

clip_image002

图片来源是http://commons.apache.org/pool/guide/classdiagrams.html。只需要看清楚几个接口的关系就可以了。

那么最好还是举例看看一个pool如何使用吧。例子是commons-pool1.4版本,jdk1.6

<代码修改自参考资料http://www.ibm.com/developerworks/cn/java/l-common-pool/>

首先定义一个对象(一般使用线程池的原则是对象比较大,所以我们就叫它大对象):

   1: /**
   2:  * 
   3:  */
   4: package common.pool;
   5:  
   6: /**
   7:  * @author Administrator
   8:  *
   9:  */
  10: public class BigObject {
  11:  
  12:     private boolean active;
  13:     
  14:     public boolean isActive() {
  15:         return active;
  16:     }
  17:  
  18:     public void setActive(boolean active) {
  19:         this.active = active;
  20:     }
  21:  
  22:     public BigObject() {
  23:         active = true;
  24:         System.out.println("i am a big object!!!");
  25:     }
  26:     
  27:     public String toString() {
  28:         return "big object "+ this.hashCode();
  29:     }
  30: }

接着去实现PoolableObjectFactory:

   1: /**
   2:  * 
   3:  */
   4: package common.pool;
   5:  
   6: import org.apache.commons.pool.*;
   7: /**
   8:  * @author Administrator
   9:  *
  10:  */
  11: public class TestFactory implements PoolableObjectFactory{
  12:  
  13:     @Override
  14:     public void activateObject(Object arg0) throws Exception {
  15:         // TODO Auto-generated method stub
  16:         ((BigObject)arg0).setActive(true);
  17:     }
  18:  
  19:     @Override
  20:     public void destroyObject(Object arg0) throws Exception {
  21:         // TODO Auto-generated method stub
  22:         arg0 = null;
  23:     }
  24:  
  25:     @Override
  26:     public Object makeObject() throws Exception {
  27:         // TODO Auto-generated method stub
  28:         BigObject bo = new BigObject();
  29:         return bo;
  30:     }
  31:  
  32:     @Override
  33:     public void passivateObject(Object arg0) throws Exception {
  34:         // TODO Auto-generated method stub
  35:         ((BigObject)arg0).setActive(false);
  36:     }
  37:  
  38:     @Override
  39:     public boolean validateObject(Object arg0) {
  40:         // TODO Auto-generated method stub
  41:         if(((BigObject)arg0).isActive())
  42:             return true;
  43:         else
  44:             return false;
  45:     }
  46:  
  47: }

最后测试:

   1: /**
   2:  * 
   3:  */
   4: package common.pool;
   5:  
   6: import org.apache.commons.pool.ObjectPool;
   7: import org.apache.commons.pool.PoolableObjectFactory;
   8: import org.apache.commons.pool.impl.StackObjectPool;
   9:  
  10:  
  11: /**
  12:  * @author Administrator
  13:  *
  14:  */
  15: public class PoolTest {
  16:  
  17:     /**
  18:      * @param args
  19:      */
  20:     public static void main(String[] args) {
  21:         // TODO Auto-generated method stub
  22:         BigObject bo = null;
  23:         PoolableObjectFactory factory = new TestFactory();
  24:         ObjectPool pool = new StackObjectPool(factory);
  25:         try {
  26:             for(long i = 0; i < 10 ; i++) {
  27:                 System.out.println("== " + i + " ==");
  28:                 bo = (BigObject) pool.borrowObject();
  29:                 System.out.println(bo);
  30:                 System.out.println(pool.getNumActive());
  31:                 if((i&1)==0) {
  32:                     pool.returnObject(bo);
  33:                 }
  34:             }
  35:            // bo = null;//明确地设为null,作为对象已归还的标志
  36:         }
  37:         catch (Exception e) {
  38:             e.printStackTrace();
  39:         }
  40:         finally {
  41:             try{
  42:                 if (bo != null) {//避免将一个对象归还两次
  43:                     pool.returnObject(bo);
  44:                 }
  45:                 pool.close();
  46:             }
  47:             catch (Exception e){
  48:                 e.printStackTrace();
  49:             }
  50:         }
  51:  
  52:     }
  53:  
  54: }
  55:  

结束了,一个pool的简单应用就搞定了。下面我们看看代码的具体实现是什么。

先看看pool构造时候都做了些什么:

StackObjectPool里有两个protected field,一个protected PoolableObjectFactory _factory = null;一个protected Stack _pool = null;。前者是通过构造方法注入的一个factory,我们的代码里已经自己实现了一个TestFactory;后者是具体的Pool管理策略,像StackObjectPool的话就是内部用这样一个Stack来管理对象。

构造时会通过

   1: public StackObjectPool(PoolableObjectFactory factory, int maxIdle, int initIdleCapacity) {
   2:         _factory = factory;
   3:         _maxSleeping = (maxIdle < 0 ? DEFAULT_MAX_SLEEPING : maxIdle);
   4:         int initcapacity = (initIdleCapacity < 1 ? DEFAULT_INIT_SLEEPING_CAPACITY : initIdleCapacity);
   5:         _pool = new Stack();
   6:         _pool.ensureCapacity( initcapacity > _maxSleeping ? _maxSleeping : initcapacity);
   7:     }

来完成初始参数和变量的构造。

接着我们开始从pool中取对象了,调用borrowObject的方法,具体实现在源码中是这样的:

   1: public synchronized Object borrowObject() throws Exception {
   2:         assertOpen();
   3:         Object obj = null;
   4:         boolean newlyCreated = false;
   5:         while (null == obj) {
   6:             if (!_pool.empty()) {
   7:                 obj = _pool.pop();
   8:             } else {
   9:                 if(null == _factory) {
  10:                     throw new NoSuchElementException();
  11:                 } else {
  12:                     obj = _factory.makeObject();
  13:                     newlyCreated = true;
  14:                   if (obj == null) {
  15:                     throw new NoSuchElementException("PoolableObjectFactory.makeObject() returned null.");
  16:                   }
  17:                 }
  18:             }
  19:             if (null != _factory && null != obj) {
  20:                 try {
  21:                     _factory.activateObject(obj);
  22:                     if (!_factory.validateObject(obj)) {
  23:                         throw new Exception("ValidateObject failed");
  24:                     }
  25:                 } catch (Throwable t) {
  26:                     try {
  27:                         _factory.destroyObject(obj);
  28:                     } catch (Throwable t2) {
  29:                         // swallowed
  30:                     } finally {
  31:                         obj = null;
  32:                     } 
  33:                     if (newlyCreated) {
  34:                         throw new NoSuchElementException(
  35:                             "Could not create a validated object, cause: " +
  36:                             t.getMessage());
  37:                     }
  38:                 }
  39:             }
  40:         }
  41:         _numActive++;
  42:         return obj;
  43:     }

在不考虑同步的时候,我们只看对象的创建,典型的几个if逻辑就完成了这样的功能,在pool空时,就用factory的makeObject方法,不空则直接从内置stack里pop一个对象出来。接着在对象构造OK后,开始激活这个对象。接着调用validate去校验,如果不合格就跑出异常,而异常的捕获中又会尝试去destroy对象,再有问题的话只有swallow掉异常了。整个pool会有一个_numActive变量来控制活着(被borrow但没被return)的对象数,每次borrow后都会加一。而循环是到obj不空时才退出的,所以obj不会为null。所以,只要你的makeObject合理的不返回null,那么这个逻辑就不会死循环。当然整个过程中似乎都会有多线程安全问题,所以整个方法都加同步。但是这显然不是完美的解决方法,对于复杂的多线程问题,调度还需要程序员自己来控制。

接着就是returnObject了,顾名思义就是把从pool中借出来的object还回去。逻辑代码如下:

   1: public synchronized void returnObject(Object obj) throws Exception {
   2:         boolean success = !isClosed();
   3:         if(null != _factory) {
   4:             if(!_factory.validateObject(obj)) {
   5:                 success = false;
   6:             } else {
   7:                 try {
   8:                     _factory.passivateObject(obj);
   9:                 } catch(Exception e) {
  10:                     success = false;
  11:                 }
  12:             }
  13:         }
  14:  
  15:         boolean shouldDestroy = !success;
  16:  
  17:         _numActive--;
  18:         if (success) {
  19:             Object toBeDestroyed = null;
  20:             if(_pool.size() >= _maxSleeping) {
  21:                 shouldDestroy = true;
  22:                 toBeDestroyed = _pool.remove(0); // remove the stalest object
  23:             }
  24:             _pool.push(obj);
  25:             obj = toBeDestroyed; // swap returned obj with the stalest one so it can be destroyed
  26:         }
  27:         notifyAll(); // _numActive has changed
  28:  
  29:         if(shouldDestroy) { // by constructor, shouldDestroy is false when _factory is null
  30:             try {
  31:                 _factory.destroyObject(obj);
  32:             } catch(Exception e) {
  33:                 // ignored
  34:             }
  35:         }
  36:     }

首先一个success变量作为判断位来决定对象能否放回pool。如果经过validate后发现对象不合格,那么显然不能放回pool了。如果validate合格,那么就尝试用factory的passivateObject方法使对象变为not active。接着进入逻辑判断如果可以放回,那么尝试将对象push到pool的内置stack中,尝试时会有阈值限定,如果超过了阈值的话,那么需要以某种策略将stack中的对象remove掉一个。而策略如果仔细看代码就会发现很合理,首先在stack中remove最老的一个,然后将这个remove值保存为临时变量,将待放回对象push到stack,接着将临时对象转接到带放回对象上。我们看到,这样的一组行为保证了几个对象的销毁,而不会出现内存泄露。

最后附上apache官网上的几张时序图来完美解释borrow和return的过程:

clip_image002[4]

clip_image002[6]

基本介绍到此,后续可能有进阶研究,期待时间~~

参考资料:

http://www.ibm.com/developerworks/cn/java/l-common-pool/

http://commons.apache.org/pool

posted on 2011-05-06 10:53 changedi 阅读(6119) 评论(6)  编辑  收藏 所属分类: Java技术

评论

# re: commons-pool学习笔记 2011-05-14 11:19 文学社

这种结构不错。  回复  更多评论   

# re: commons-pool学习笔记[未登录] 2011-11-09 22:08 刺猬

我大致浏览了下代码,有两个问题求解释:
1、在判断idlemax时候,如果到达阈值,remove的是栈底元素,Java stack使用vector实现的,如果remove数组的第一个元素,恐怕在一些vector size比较大情况下性能堪忧,再者,stack里面保留的都是idle状态的元素,取处于idle最长的意义进行删除何在? 这又不是LRU
2、notifyAll 意义? 这个是通知谁 ?   回复  更多评论   

# re: commons-pool学习笔记[未登录] 2011-11-10 13:54 changedi

@刺猬
1.我想性能问题不算是个问题,这种池结构的设计,只要是这种线性表结构,都难免不了一次remove的数组拷贝,不过一般对象池的维护,大小是自己定的~~肯定不会太大,为了复用,否则new新的对象,交给JVM管理也就是了。
2.这个问题可以写个多线程测试的例子,其中同步使用对象锁,锁对象就是每个线程应该持有的这个公有的pool。这样,每次调用pool.wait()就可以让线程hold等待,在条件满足时调用一次pool.returnObject(),这样的notifyAll被调用,一切就都正常了~~
愚见,欢迎讨论  回复  更多评论   

# re: commons-pool学习笔记[未登录] 2011-11-10 23:34 刺猬

谢谢博主答复
这两点我是这么看的
1、其实我从开始就觉得这个类比较鸡肋,没啥大用处。对于这个vector的使用我还是保留意见,认为没有必要使用,一则清除老idle对象没有理论依据(或者是至少我没想到有这种理论依据),二则这里又使用的是vector,如果一定要清楚老对象,完全可以使用linklist之类,在删除对象可以到O(1)时间复杂度。退者说,没有实际应用说明我们的对象池对象不会很多,如果我要维持一个size为32的数据库连接池,对于一些高并发的数据库请求,会不会出现频繁的对象新建和释放。
2、我赞成博主这个说法,我最初也是这样猜想,但是如果继续深挖下去,这个说法同样困惑:notify的方法不止出现在对象归还逻辑中,在invalidate对象时同样会notify操作。Pool调用wait无非是获取空闲对象发现pool里面没有空闲对象,释放Pool锁进行等待出现空闲对象。那invalidate对象又是通知什么? 再者说,为什么调用Pool的客户端需要自己控制Pool内部对象的同步互斥——这些本应该直接封装在borrow方法中的逻辑。从设计上说令人疑惑。
以前做C的 Java 才入门 有些问题说的不是很清楚 博主见谅   回复  更多评论   

# re: commons-pool学习笔记[未登录] 2011-11-11 14:15 changedi

@刺猬
深有同感,针对这个同步的问题,其实回答你的时候就发现它恶心了,你说的很对,要么完全交给调用者管理,要么就完全自己管理,这样半拿半放的让人困惑~不过代码简单,而且又是比较老的东西,能吸收多少就看大家自己了~毕竟是commons下的,可惜了  回复  更多评论   

# re: commons-pool学习笔记[未登录] 2011-11-11 23:08 刺猬

谢谢博主的答复
回头看了下genericpool,这个代码质量好多了  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航: