使用Jakarta Commons Pool处理对象池化(转)

孙海涛 (alexhsun@hotmail.com),

2003 年 12 月 12 日

恰当地使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率。Jakarta Commons Pool组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现,可以有效地减少处理对象池化时的工作量,为其它重要的工作留下更多的精力和时间。

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

对象池化技术

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

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

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

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的知识和经验,对语法和基本概念(对象、异常、类、接口、实例、继承和实现等)有一般了解即可。


跳转到主要内容

developerWorks 中国 > Java technology >
使用Jakarta Commons Pool处理对象池化
developerWorks


文档选项
将打印机的版面设置成横向打印模式

打印本页
将此页作为电子邮件发送

将此页作为电子邮件发送

级别: 初级

孙海涛 (alexhsun@hotmail.com),

2003 年 12 月 12 日

恰当地使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率。Jakarta Commons Pool组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现,可以有效地减少处理对象池化时的工作量,为其它重要的工作留下更多的精力和时间。

创建新的对象并初始化的操作,可能会消耗很多的时间。在这种对象的初始化工作包含了一些费时的操作(例如,从一台位于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外,还需要先准备下列一些东西:

* Jakarta Commons Pool
o 所需版本:1.0.1+
o 下载地址: http://jakarta.apache.org/commons/pool
o 作用:处理对象池化
* Jakarta Commons Collections
o 所需版本:2.1+
o 下载地址: http://jakarta.apache.org/commons/collections
o 作用:支持Jakarta Commons Pool的运行

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

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

* Ant
o 所需版本:1.5.3+
o 下载地址: http://ant.apache.org
o 作用:运行编译用脚本
* JUnit
o 所需版本:3.8.1+
o 下载地址: http://www.junit.org
o 作用:编译和运行单元测试

具体的编译方法,可以参看有关的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也各有各自的特色,分别适应不同的情况。




文档选项
将打印机的版面设置成横向打印模式

打印本页
将此页作为电子邮件发送

将此页作为电子邮件发送

级别: 初级

孙海涛 (alexhsun@hotmail.com),

2003 年 12 月 12 日

恰当地使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率。Jakarta Commons Pool组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现,可以有效地减少处理对象池化时的工作量,为其它重要的工作留下更多的精力和时间。

创建新的对象并初始化的操作,可能会消耗很多的时间。在这种对象的初始化工作包含了一些费时的操作(例如,从一台位于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外,还需要先准备下列一些东西:

* Jakarta Commons Pool
o 所需版本:1.0.1+
o 下载地址: http://jakarta.apache.org/commons/pool
o 作用:处理对象池化
* Jakarta Commons Collections
o 所需版本:2.1+
o 下载地址: http://jakarta.apache.org/commons/collections
o 作用:支持Jakarta Commons Pool的运行

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

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

* Ant
o 所需版本:1.5.3+
o 下载地址: http://ant.apache.org
o 作用:运行编译用脚本
* JUnit
o 所需版本:3.8.1+
o 下载地址: http://www.junit.org
o 作用:编译和运行单元测试

具体的编译方法,可以参看有关的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)。其中:
o 参数factory指明要与之配合使用的PoolableObjectFactory实例;
o 参数max设定可保存对象数目的上限;
o 参数init则指明初始的参考大小。
* 剩余的四个构造方法则是最复杂的构造方法在某方面的简化版本,可以根据需要选用。它们是:
o StackObjectPool(int max)
o StackObjectPool(int max, int init)
o StackObjectPool(PoolableObjectFactory factory)
o StackObjectPool(PoolableObjectFactory factory, int max)

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

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




文档选项
将打印机的版面设置成横向打印模式

打印本页
将此页作为电子邮件发送

将此页作为电子邮件发送

级别: 初级

孙海涛 (alexhsun@hotmail.com),

2003 年 12 月 12 日

恰当地使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率。Jakarta Commons Pool组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现,可以有效地减少处理对象池化时的工作量,为其它重要的工作留下更多的精力和时间。

创建新的对象并初始化的操作,可能会消耗很多的时间。在这种对象的初始化工作包含了一些费时的操作(例如,从一台位于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外,还需要先准备下列一些东西:

* Jakarta Commons Pool
o 所需版本:1.0.1+
o 下载地址: http://jakarta.apache.org/commons/pool
o 作用:处理对象池化
* Jakarta Commons Collections
o 所需版本:2.1+
o 下载地址: http://jakarta.apache.org/commons/collections
o 作用:支持Jakarta Commons Pool的运行

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

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

* Ant
o 所需版本:1.5.3+
o 下载地址: http://ant.apache.org
o 作用:运行编译用脚本
* JUnit
o 所需版本:3.8.1+
o 下载地址: http://www.junit.org
o 作用:编译和运行单元测试

具体的编译方法,可以参看有关的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)。其中:
o 参数factory指明要与之配合使用的PoolableObjectFactory实例;
o 参数max设定可保存对象数目的上限;
o 参数init则指明初始的参考大小。
* 剩余的四个构造方法则是最复杂的构造方法在某方面的简化版本,可以根据需要选用。它们是:
o StackObjectPool(int max)
o StackObjectPool(int max, int init)
o StackObjectPool(PoolableObjectFactory factory)
o 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)。其中:
o 参数factory指明要与之配合使用的PoolableObjectFactory实例
o 参数initSize则指明初始化时在池中创建多少个对象。
* 剩下的一个构造方法,则是最复杂的构造方法在某方面的简化版本,适合在大多数情况下使用。它是:
o SoftReferenceObjectPool(PoolableObjectFactory factory)

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

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


GenericObjectPool

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

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

GenericObjectPool的构造方法共有七个,其中:

* 最简单的一个是GenericObjectPool(PoolableObjectFactory factory)。仅仅指明要用的PoolableObjectFactory实例,其它参数则采用默认值。
* 最复杂的一个是GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle)。其中:
o 参数factory指明要与之配合使用的PoolableObjectFactory实例。
o 参数maxActive指明能从池中借出的对象的最大数目。如果这个值不是正数,表示没有限制。
o 参数whenExhaustedAction指定在池中借出对象的数目已达极限的情况下,调用它的 borrowObject方法时的行为。可以选用的值有:
+ GenericObjectPool.WHEN_EXHAUSTED_BLOCK,表示等待;
+ GenericObjectPool.WHEN_EXHAUSTED_GROW,表示创建新的实例(不过这就使 maxActive参数失去了意义);
+ GenericObjectPool.WHEN_EXHAUSTED_FAIL,表示抛出一个 java.util.NoSuchElementException异常。
o 参数maxWait指明若在对象池空时调用borrowObject方法的行为被设定成等待,最多等待多少毫秒。如果等待时间超过了这个数值,则会抛出一个java.util.NoSuchElementException异常。如果这个值不是正数,表示无限期等待。
o 参数testOnBorrow设定在借出对象时是否进行有效性检查。
o 参数testOnBorrow设定在还回对象时是否进行有效性检查。
o 参数timeBetweenEvictionRunsMillis,设定间隔每过多少毫秒进行一次后台对象清理的行动。如果这个值不是正数,则实际上不会进行后台对象清理。
o 参数numTestsPerEvictionRun,设定在进行后台对象清理时,每次检查几个对象。如果这个值不是正数,则每次检查的对象数是检查时池内对象的总数乘以这个值的负倒数再向上取整的结果――也就是说,如果这个值是-2(-3、-4、-5……)的话,那么每次大约检查当时池内对象总数的1/2(1/3、1/4、1/5……)左右。
o 参数minEvictableIdleTimeMillis,设定在进行后台对象清理时,视休眠时间超过了多少毫秒的对象为过期。过期的对象将被回收。如果这个值不是正数,那么对休眠时间没有特别的约束。
o 参数testWhileIdle,则设定在进行后台对象清理时,是否还对没有过期的池内对象进行有效性检查。不能通过有效性检查的对象也将被回收。
o 另一个比较特别的构造方法是GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) 。其中:
+ 参数factory指明要与之配合使用的PoolableObjectFactory实例;
+ 参数config则指明一个包括了各个参数的预设值的对象(详见《GenericObjectPool.Config》一节)。
o 剩下的五个构造函数则是最复杂的构造方法在某方面的简化版本,可以根据情况选用。它们是:
+ GenericObjectPool(PoolableObjectFactory factory, int maxActive)
+ GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait)
+ GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn)
+ GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle)
+ GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn)

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


GenericObjectPool.Config

调用一个有很多的参数的方法的时候,很可能将参数的位置和个数搞错,导致编译或运行时的错误;阅读包含了有很多参数的方法调用的代码的时候,也很可能因为没有搞对参数的位置和个数,产生错误的理解。因此,人们往往避免给一个方法安排太多的参数的做法(所谓的“Long Parameter List”)。不过,有些方法又确实需要许多参数才能完成工作。于是,就有人想到了一种将大批的参数封装到一个对象(称为参数对象,Parameter Object)里,然后将这个对象作为单一的参数传递的两全其美的对策。

因为生成GenericKeyedObjectPool时可供设置的特性非常之多,所以它的构造方法里也就难免会需要不少的参数。 GenericKeyedObjectPool除了提供了几个超长的构造方法之外,同时也定义了一个使用参数对象的构造方法。所用参数对象的类型是 GenericKeyedObjectPool.Config。

GenericKeyedObjectPool.Config定义了许多的public字段,每个对应一种可以为 GenericKeyedObjectPool设置的特性,包括:

* int maxActive
* int maxIdle
* long maxWait
* long minEvictableIdleTimeMillis
* int numTestsPerEvictionRun
* boolean testOnBorrow
* boolean testOnReturn
* boolean testWhileIdle
* long timeBetweenEvictionRunsMillis
* byte whenExhaustedAction

这些字段的作用,与在GenericKeyedObjectPool最复杂的构造方法中与它们同名的参数完全相同。

使用的时候,先生成一个GenericKeyedObjectPool.Config对象,然后将个字段设置为想要的值,最后用这个对象作为唯一的参数调用GenericKeyedObjectPool的构造方法即可。

注意:使用有许多public字段、却没有任何方法的类,也是一个人们往往加以避免的行为(所谓的“Data Class”)。不过这次GenericKeyedObjectPool特立独行了一回。


带键值的对象池

有时候,单用对池内所有对象一视同仁的对象池,并不能解决的问题。例如,对于一组某些参数设置不同的同类对象――比如一堆指向不同地址的 java.net.URL对象或者一批代表不同语句的java.sql.PreparedStatement对象,用这样的方法池化,就有可能取出不合用的对象的麻烦。

可以通过为每一组参数相同的同类对象建立一个单独的对象池来解决这个问题。但是,如果使用普通的ObjectPool来实施这个计策的话,因为普通的 PoolableObjectFactory只能生产出大批设置完全一致的对象,就需要为每一组参数相
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.m或d论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 、1资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值