java池化

以下图片来源于http://commons.apache.org/pool/guide/sequencediagrams.html,加了点注释而已

所有的时间都是毫秒,并且 GenericObjectPool 是 thread safe,以下分别说明各个参数:

whenExhaustedAction
   WHEN_EXHAUSTED_BLOCK
      参考 maxWait
   WHEN_EXHAUSTED_FAIL
   WHEN_EXHAUSTED_GROW

maxWait
   如果 whenExhaustedAction = WHEN_EXHAUSTED_BLOCK,那么 borrow 不能立即返回 Object时,
   会wait,知道得到池化对象,或者超过 maxWait。如果maxWait < 1,那么 会一直等到获得池化对象

lifo
   true/false

testOnBorrow
   GenericObjectPool.borrowObject() 时 是否调用 PoolableObjectFactory.validateObject



testOnReturn
   GenericObjectPool.returnObject() 时 是否调用 PoolableObjectFactory.validateObject


maxIdle 默认值为 DEFAULT_MAX_IDLE=8,超过该值的 idle 对象会被destory

基本上看到这里就能用了,最好别用 evictor,除非要求..... 例如 db长连接、心跳

 

 


timeBetweenEvictionRunsMillis
   启动evictor thread的间隔,如果设置为<1不会启动evictor thread
   evictor thread 按照规则下面定义来 填充/移除 池中的对象
   evictor thread 在执行检查时会阻塞 borrow/return,因此如果该参数设置的很小,会造成evictor thread频繁执行而影响性能
      Eviction runs require an exclusive synchronization
      lock on the pool, so if they run too frequently and / or incur excessive
      latency when creating, destroying or validating object instances,
      performance issues may result

minIdle
   如果evictor启动,evictor检查到池中的对象小于该值时,会创建min(minIdle, numActive + numIdle - maxActive) 的对象来填充池。小心使用该变量,设置为 <1最安全,可能导致dbcp死锁:dbcp-44,在common-pool 1.5 dbcp1.4中已经修正了这个问题,4年

numTestsPerEvictionRun 
   如果evictor启动,evictor每次启动时会检查池中的 x 个对象
           numTestsPerEvictionRun > 0, x = min(_numTestsPerEvictionRun, GenericObjectPool.getNumIdle())
      numTestsPerEvictionRun < 0, x = ceil( GenericObjectPool.getNumIdle())/abs(numTestsPerEvictionRun) )
         i.e. abs(numTestsPerEvictionRun) 分之一的空闲对象会被检查

minEvictableIdleTimeMillis
   如果evictor启动,evictor每次检查时可能会将池中空闲太久的对象移除池
      如果设置为 < 1,是否把 idle 对象移除要看 softMinEvictableIdleTimeMillis
      如果设置为 > 0,那么会把闲置了 minEvictableIdleTimeMillis 的对象PoolableObjectFactory.destroyObject掉

softMinEvictableIdleTimeMillis
   如果evictor启动,并且minEvictableIdleTimeMillis < 1 或者按照 minEvictableIdleTimeMillis 计算对象不需要移出池
      如果设置为 < 1,则不会移除任何idle对象
      如果设置为 > 0,则当对象闲置的时间超过softMinEvictableIdleTimeMillis 且 GenericObjectPool.getNumIdle > minIdle 时 将对象移除池
一般应该把minEvictableIdleTimeMillis 配置为 -1,把softMinEvictableIdleTimeMillis配置为整数


_testWhileIdle
   如果evictor启动,该变量为true则调用 PoolableObjectFactory.validateObject

备注1,minEvictableIdleTimeMillis 和 minEvictableIdleTimeMillis 的使用方法
if ((getMinEvictableIdleTimeMillis() > 0) &&
    (idleTimeMilis > getMinEvictableIdleTimeMillis())) {
removeObject = true;
} else if ((getSoftMinEvictableIdleTimeMillis() > 0) &&
    (idleTimeMilis > getSoftMinEvictableIdleTimeMillis()) &&
    ((getNumIdle() + 1)> getMinIdle())) { // +1 accounts for object we are processing
removeObject = true;

 

 

创建新的对象并初始化的操作,可能会消耗很多的时间。在这种对象的初始化工作包含了一些费时的操作(例如,从一台位于20,000千米以外的主机上读出一些数据)的时候,尤其是这样。在需要大量生成这样的对象的时候,就可能会对性能造成一些不可忽略的影响。要缓解这个问题,除了选用更好的硬件和更棒的虚拟机以外,适当地采用一些能够减少对象创建次数的编码技巧,也是一种有效的对策。对象池化技术(Object Pooling)就是这方面的著名技巧,而Jakarta Commons Pool组件则是处理对象池化的得力外援。

对象池化技术

对象池化的基本思路是:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度上减少频繁创建对象所造成的开销。用于充当保存对象的“容器”的对象,被称为“对象池”(Object Pool,或简称Pool)。

对于没有状态的对象(例如String),在重复使用之前,无需进行任何处理;对于有状态的对象(例如StringBuffer),在重复使用之前,就需要把它们恢复到等同于刚刚生成时的状态。由于条件的限制,恢复某个对象的状态的操作不可能实现了的话,就得把这个对象抛弃,改用新创建的实例了。

并非所有对象都适合拿来池化――因为维护对象池也要造成一定开销。对生成时开销不大的对象进行池化,反而可能会出现“维护对象池的开销”大于“生成新对象的开销”,从而使性能降低的情况。但是对于生成时开销可观的对象,池化技术就是提高性能的有效策略了。

When to use sidebars

说明:英语中的Pool除了“池”之外,还有“供多方共享的资源”意思。作者十分怀疑第二种才是“Object Pool”中的Pool的实际含义,但是“对象池”的说法已经广为流传,而一时又没有足以替代的贴切译法,因此这里仍然沿用这种译名。

Jakarta Commons Pool组件

Jakarta Commons Pool是一个用于在Java程序中实现对象池化的组件。它的基本情况是:

  • 主要作者:Morgan Delagrange、Geir Magnusson、Craig McClanahan、Rodney Waldhoff、David Weinrich和Dirk Verbeeck
  • 最新版本:1.1
  • 所含包数:2个(org.apache.commons.pool和org.apache.commons.pool.impl)
  • 所含类数:21个(其中有4个抽象类和6个接口)
  • 适用平台:Java 2, Standard Edition.
  • 单纯地使用Pool组件不需要太多的Java 2的知识和经验,对语法和基本概念(对象、异常、类、接口、实例、继承和实现等)有一般了解即可。

下载和安装

为了顺利的按照本文中提到的方法使用Pool组件,除去Java 2 SDK外,还需要先准备下列一些东西:

以上两种软件均有已编译包和源代码包两种形式可供选择。一般情况下,使用已编译包即可。不过建议同时也下载源代码包,作为参考资料使用。

如果打算使用源代码包自行编译,那么还需要准备以下一些东西:

具体的编译方法,可以参看有关的Ant文档。

将解压或编译后得到的commons-pool.jar和commons-collections.jar放入CLASSPATH,就可以开始使用Pool组件了。

PoolableObjectFactory、ObjectPool和ObjectPoolFactory

在Pool组件中,对象池化的工作被划分给了三类对象:

  • PoolableObjectFactory用于管理被池化的对象的产生、激活、挂起、校验和销毁;
  • ObjectPool用于管理要被池化的对象的借出和归还,并通知PoolableObjectFactory完成相应的工作;
  • ObjectPoolFactory则用于大量生成相同类型和设置的ObjectPool。

相应地,使用Pool组件的过程,也大体可以划分成“创立PoolableObjectFactory”、“使用ObjectPool”和可选的“利用ObjectPoolFactory”三种动作。

创立PoolableObjectFactory

Pool组件利用PoolableObjectFactory来照看被池化的对象。ObjectPool的实例在需要处理被池化的对象的产生、激活、挂起、校验和销毁工作时,就会调用跟它关联在一起的PoolableObjectFactory实例的相应方法来操作。

PoolableObjectFactory是在org.apache.commons.pool包中定义的一个接口。实际使用的时候需要利用这个接口的一个具体实现。Pool组件本身没有包含任何一种PoolableObjectFactory实现,需要根据情况自行创立。

创立PoolableObjectFactory的大体步骤是:

  1. 创建一个实现了PoolableObjectFactory接口的类。

     

    import org.apache.commons.pool.PoolableObjectFactory;
    public class PoolableObjectFactorySample
            implements PoolableObjectFactory {
        private static int counter = 0;
    }
    

     

  2. 为这个类添加一个Object makeObject()方法。这个方法用于在必要时产生新的对象。

     

    public Object makeObject() throws Exception {
        Object obj = String.valueOf(counter++);
        System.err.println("Making Object " + obj);
        return obj;
    }
    

     

  3. 为这个类添加一个void activateObject(Object obj)方法。这个方法用于将对象“激活”――设置为适合开始使用的状态。

     

    public void activateObject(Object obj) throws Exception {
        System.err.println("Activating Object " + obj);
    }
    

     

  4. 为这个类添加一个void passivateObject(Object obj)方法。这个方法用于将对象“挂起”――设置为适合开始休眠的状态。

     

    public void passivateObject(Object obj) throws Exception {
        System.err.println("Passivating Object " + obj);
    }
    

     

  5. 为这个类添加一个boolean validateObject(Object obj)方法。这个方法用于校验一个具体的对象是否仍然有效,已失效的对象会被自动交给destroyObject方法销毁

     

    public boolean validateObject(Object obj) {
        boolean result = (Math.random() > 0.5);
        System.err.println("Validating Object "
                + obj + " : " + result);
        return result;
    }
    

     

  6. 为这个类添加一个void destroyObject(Object obj)方法。这个方法用于销毁被validateObject判定为已失效的对象。

     

    public void destroyObject(Object obj) throws Exception {
        System.err.println("Destroying Object " + obj);
    }
    

     

最后完成的PoolableObjectFactory类似这个样子:

 

								
          PoolableObjectFactorySample.java
							
        

import org.apache.commons.pool.PoolableObjectFactory;
public class PoolableObjectFactorySample
        implements PoolableObjectFactory {
    private static int counter = 0;
    public Object makeObject() throws Exception {
        Object obj = String.valueOf(counter++);
        System.err.println("Making Object " + obj);
        return obj;
    }
    public void activateObject(Object obj) throws Exception {
        System.err.println("Activating Object " + obj);
    }
    public void passivateObject(Object obj) throws Exception {
        System.err.println("Passivating Object " + obj);
    }
    public boolean validateObject(Object obj) {
        /* 以1/2的概率将对象判定为失效 */
        boolean result = (Math.random() > 0.5);
        System.err.println("Validating Object "
                + obj + " : " + result);
        return result;
    }
    public void destroyObject(Object obj) throws Exception {
        System.err.println("Destroying Object " + obj);
    }
}

 

使用ObjectPool

有了合适的PoolableObjectFactory之后,便可以开始请出ObjectPool来与之同台演出了。

ObjectPool是在org.apache.commons.pool包中定义的一个接口,实际使用的时候也需要利用这个接口的一个具体实现。Pool组件本身包含了若干种现成的ObjectPool实现,可以直接利用。如果都不合用,也可以根据情况自行创建。具体的创建方法,可以参看Pool组件的文档和源码。

ObjectPool的使用方法类似这样:

  1. 生成一个要用的PoolableObjectFactory类的实例。

     

    PoolableObjectFactory factory = new PoolableObjectFactorySample();
    

     

  2. 利用这个PoolableObjectFactory实例为参数,生成一个实现了ObjectPool接口的类(例如StackObjectPool)的实例,作为对象池。

     

    ObjectPool pool = new StackObjectPool(factory);
    

     

  3. 需要从对象池中取出对象时,调用该对象池的Object borrowObject()方法。

     

    Object obj = null;
    obj = pool.borrowObject();
    

     

  4. 需要将对象放回对象池中时,调用该对象池的void returnObject(Object obj)方法。

     

    pool.returnObject(obj);
    

     

  5. 当不再需要使用一个对象池时,调用该对象池的void close()方法,释放它所占据的资源。

     

    pool.close();
    

     

这些操作都可能会抛出异常,需要另外处理。

比较完整的使用ObjectPool的全过程,可以参考这段代码:

 

								
          ObjectPoolSample.java 
							
        

import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.StackObjectPool;
public class ObjectPoolSample {
    public static void main(String[] args) {
        Object obj = null;
        PoolableObjectFactory factory 
                = new PoolableObjectFactorySample();
        ObjectPool pool = new StackObjectPool(factory);
        try {
            for(long i = 0; i < 100 ; i++) {
                System.out.println("== " + i + " ==");
                obj = pool.borrowObject();
                System.out.println(obj);
                pool.returnObject(obj);
            }
            obj = null;//明确地设为null,作为对象已归还的标志
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            try{
                if (obj != null) {//避免将一个对象归还两次
                    pool.returnObject(obj);
                }
                pool.close();
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

 

另外,ObjectPool接口还定义了几个可以由具体的实现决定要不要支持的操作,包括:

void clear()

清除所有当前在此对象池中休眠的对象。

int getNumActive()

返回已经从此对象池中借出的对象的总数。

int getNumIdle()

返回当前在此对象池中休眠的对象的数目。

void setFactory(PoolableObjectFactory factory)

将当前对象池与参数中给定的PoolableObjectFactory相关联。如果在当前状态下,无法完成这一操作,会有一个IllegalStateException异常抛出。

利用ObjectPoolFactory

有时候,要在多处生成类型和设置都相同的ObjectPool。如果在每个地方都重写一次调用相应构造方法的代码,不但比较麻烦,而且日后修改起来,也有所不便。这种时候,正是使用ObjectPoolFactory的时机。

ObjectPoolFactory是一个在org.apache.commons.pool中定义的接口,它定义了一个称为ObjectPool createPool()方法,可以用于大量生产类型和设置都相同的ObjectPool。

Pool组件中,对每一个ObjectPool实现,都有一个对应的ObjectPoolFactory实现。它们相互之间,有一一对应的参数相同的构造方法。使用的时候,只要先用想要的参数和想用的ObjectPoolFactory实例,构造出一个ObjectPoolFactory对象,然后在需要生成ObjectPool的地方,调用这个对象的createPool()方法就可以了。日后无论想要调整所用ObjectPool的参数还是类型,只需要修改这一处,就可以大功告成了。

将 《使用ObjectPool》一节中的例子,改为使用ObjectPoolFactory来生成所用的ObjectPool对象之后,基本就是这种形式:

 

ObjectPoolFactorySample.java

import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.ObjectPoolFactory;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.StackObjectPoolFactory;
public class ObjectPoolFactorySample {
    public static void main(String[] args) {
        Object obj = null;
        PoolableObjectFactory factory
                = new PoolableObjectFactorySample();
        ObjectPoolFactory poolFactory
                = new StackObjectPoolFactory(factory);
        ObjectPool pool = poolFactory.createPool();
        try {
            for(long i = 0; i < 100 ; i++) {
                System.out.println("== " + i + " ==");
                obj = pool.borrowObject();
                System.out.println(obj);
                pool.returnObject(obj);
            }
            obj = null;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            try{
                if (obj != null) {
                    pool.returnObject(obj);
                }
                pool.close();
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

 

借助BasePoolableObjectFactory

PoolableObjectFactory定义了许多方法,可以适应多种不同的情况。但是,在并没有什么特殊需要的时候,直接实现PoolableObjectFactory接口,就要编写若干的不进行任何操作,或是始终返回true的方法来让编译通过,比较繁琐。这种时候就可以借助BasePoolableObjectFactory的威力,来简化编码的工作。

BasePoolableObjectFactory是org.apache.commons.pool包中的一个抽象类。它实现了PoolableObjectFactory接口,并且为除了makeObject之外的方法提供了一个基本的实现――activateObject、passivateObject和destroyObject不进行任何操作,而validateObject始终返回true。通过继承这个类,而不是直接实现PoolableObjectFactory接口,就可以免去编写一些只起到让编译通过的作用的代码的麻烦了。

这个例子展示了一个从BasePoolableObjectFactory扩展而来的PoolableObjectFactory:

 

BasePoolableObjectFactorySample.java 

import org.apache.commons.pool.BasePoolableObjectFactory;
public class BasePoolableObjectFactorySample 
        extends BasePoolableObjectFactory {
    private int counter = 0;
    public Object makeObject() throws Exception {
        return String.valueOf(counter++);
    }
}

 

各式各样的ObjectPool

可口可乐公司的软饮料有可口可乐、雪碧和芬达等品种,百事可乐公司的软饮料有百事可乐、七喜和美年达等类型,而Pool组件提供的ObjectPool实现则有StackObjectPool、SoftReferenceObjectPool和GenericObjectPool等种类。

不同类型的软饮料各有各自的特点,分别适应不同消费者的口味;而不同类型的ObjectPool也各有各自的特色,分别适应不同的情况。

StackObjectPool

StackObjectPool利用一个java.util.Stack对象来保存对象池里的对象。这种对象池的特色是:

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

StackObjectPool的构造方法共有六个,其中:

  • 最简单的一个是StackObjectPool(),一切采用默认的设置,也不指明要用的PoolableObjectFactory实例。
  • 最复杂的一个则是StackObjectPool(PoolableObjectFactory factory, int max, int init)。其中:
    • 参数factory指明要与之配合使用的PoolableObjectFactory实例;
    • 参数max设定可保存对象数目的上限;
    • 参数init则指明初始的参考大小。
  • 剩余的四个构造方法则是最复杂的构造方法在某方面的简化版本,可以根据需要选用。它们是:
    • StackObjectPool(int max)
    • StackObjectPool(int max, int init)
    • StackObjectPool(PoolableObjectFactory factory)
    • StackObjectPool(PoolableObjectFactory factory, int max)

用不带factory参数的构造方法构造的StackObjectPool实例,必须要在用它的setFactory(PoolableObjectFactory factory)方法与某一PoolableObjectFactory实例关联起来后才能正常使用。

这种对象池可以在没有Jakarta Commmons Collections组件支持的情况下正常运行。

SoftReferenceObjectPool

SoftReferenceObjectPool利用一个java.util.ArrayList对象来保存对象池里的对象。不过它并不在对象池里直接保存对象本身,而是保存它们的“软引用”(Soft Reference)。这种对象池的特色是:

  • 可以保存任意多个对象,不会有容量已满的情况发生。
  • 在对象池已空的时候,调用它的borrowObject方法,会自动返回新创建的实例。
  • 可以在初始化同时,在池内预先创建一定量的对象。
  • 当内存不足的时候,池中的对象可以被Java虚拟机回收。

SoftReferenceObjectPool的构造方法共有三个,其中:

  • 最简单的是SoftReferenceObjectPool(),不预先在池内创建对象,也不指明要用的PoolableObjectFactory实例。
  • 最复杂的一个则是SoftReferenceObjectPool(PoolableObjectFactory factory, int initSize)。其中:
    • 参数factory指明要与之配合使用的PoolableObjectFactory实例
    • 参数initSize则指明初始化时在池中创建多少个对象。
  • 剩下的一个构造方法,则是最复杂的构造方法在某方面的简化版本,适合在大多数情况下使用。它是:
    • SoftReferenceObjectPool(PoolableObjectFactory factory)

用不带factory参数的构造方法构造的SoftReferenceObjectPool实例,也要在用它的setFactory(PoolableObjectFactory factory)方法与某一PoolableObjectFactory实例关联起来后才能正常使用。

这种对象池也可以在没有Jakarta Commmons Collections组件支持的情况下正常运行。

GenericObjectPool

GenericObjectPool利用一个org.apache.commons.collections.CursorableLinkedList对象来保存对象池里的对象。这种对象池的特色是:

  • 可以设定最多能从池中借出多少个对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值