日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

对象池commons-pool

發(fā)布時(shí)間:2024/4/17 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 对象池commons-pool 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

http://blog.sina.com.cn/s/blog_4b38e200010008s9.html

對(duì)象池化技術(shù)
Jakarta Commons Pool組件
下載和安裝
PoolableObjectFactory、ObjectPool和ObjectPoolFactory
創(chuàng)立PoolableObjectFactory
使用ObjectPool
利用ObjectPoolFactory
借助BasePoolableObjectFactory
各式各樣的ObjectPool
StackObjectPool
SoftReferenceObjectPool
GenericObjectPool
GenericObjectPool.Config
帶鍵值的對(duì)象池
當(dāng)出借少于歸還
線程安全問(wèn)題
什么時(shí)候不要池化
結(jié)束語(yǔ)
參考資料
關(guān)于作者
對(duì)于本文的評(píng)價(jià)

孫海濤 (alexhsun@hotmail.com)

2003 年 12 月

恰當(dāng)?shù)厥褂脤?duì)象池化技術(shù),可以有效地減少對(duì)象生成和初始化時(shí)的消耗,提高系統(tǒng)的運(yùn)行效率。Jakarta Commons Pool組件提供了一整套用于實(shí)現(xiàn)對(duì)象池化的框架,以及若干種各具特色的對(duì)象池實(shí)現(xiàn),可以有效地減少處理對(duì)象池化時(shí)的工作量,為其它重要的工作留下更多的精力和時(shí)間。
創(chuàng)建新的對(duì)象并初始化的操作,可能會(huì)消耗很多的時(shí)間。在這種對(duì)象的初始化工作包含了一些費(fèi)時(shí)的操作(例如,從一臺(tái)位于20,000千米以外的主機(jī)上讀出一些數(shù)據(jù))的時(shí)候,尤其是這樣。在需要大量生成這樣的對(duì)象的時(shí)候,就可能會(huì)對(duì)性能造成一些不可忽略的影響。要緩解這個(gè)問(wèn)題,除了選用更好的硬件和更棒的虛擬機(jī)以外,適當(dāng)?shù)夭捎靡恍┠軌驕p少對(duì)象創(chuàng)建次數(shù)的編碼技巧,也是一種有效的對(duì)策。對(duì)象池化技術(shù)(Object Pooling)就是這方面的著名技巧,而Jakarta Commons Pool組件則是處理對(duì)象池化的得力外援。

對(duì)象池化技術(shù)
對(duì)象池化的基本思路是:將用過(guò)的對(duì)象保存起來(lái),等下一次需要這種對(duì)象的時(shí)候,再拿出來(lái)重復(fù)使用,從而在一定程度上減少頻繁創(chuàng)建對(duì)象所造成的開(kāi)銷。用于充當(dāng)保存對(duì)象的“容器”的對(duì)象,被稱為“對(duì)象池”(Object Pool,或簡(jiǎn)稱Pool)。

對(duì)于沒(méi)有狀態(tài)的對(duì)象(例如String),在重復(fù)使用之前,無(wú)需進(jìn)行任何處理;對(duì)于有狀態(tài)的對(duì)象(例如StringBuffer),在重復(fù)使用之前,就需要把它們恢復(fù)到等同于剛剛生成時(shí)的狀態(tài)。由于條件的限制,恢復(fù)某個(gè)對(duì)象的狀態(tài)的操作不可能實(shí)現(xiàn)了的話,就得把這個(gè)對(duì)象拋棄,改用新創(chuàng)建的實(shí)例了。

并非所有對(duì)象都適合拿來(lái)池化??因?yàn)榫S護(hù)對(duì)象池也要造成一定開(kāi)銷。對(duì)生成時(shí)開(kāi)銷不大的對(duì)象進(jìn)行池化,反而可能會(huì)出現(xiàn)“維護(hù)對(duì)象池的開(kāi)銷”大于“生成新對(duì)象的開(kāi)銷”,從而使性能降低的情況。但是對(duì)于生成時(shí)開(kāi)銷可觀的對(duì)象,池化技術(shù)就是提高性能的有效策略了。

說(shuō)明:英語(yǔ)中的Pool除了“池”之外,還有“供多方共享的資源”意思。作者十分懷疑第二種才是“Object Pool”中的Pool的實(shí)際含義,但是“對(duì)象池”的說(shuō)法已經(jīng)廣為流傳,而一時(shí)又沒(méi)有足以替代的貼切譯法,因此這里仍然沿用這種譯名。
Jakarta Commons Pool組件


Jakarta Commons Pool是一個(gè)用于在Java程序中實(shí)現(xiàn)對(duì)象池化的組件。它的基本情況是:

主要作者:Morgan Delagrange、Geir Magnusson、Craig McClanahan、Rodney Waldhoff、David Weinrich和Dirk Verbeeck


最新版本:1.1


所含包數(shù):2個(gè)(org.apache.commons.pool和org.apache.commons.pool.impl)


所含類數(shù):21個(gè)(其中有4個(gè)抽象類和6個(gè)接口)


適用平臺(tái):Java 2, Standard Edition.


單純地使用Pool組件不需要太多的Java 2的知識(shí)和經(jīng)驗(yàn),對(duì)語(yǔ)法和基本概念(對(duì)象、異常、類、接口、實(shí)例、繼承和實(shí)現(xiàn)等)有一般了解即可。


下載和安裝
為了順利的按照本文中提到的方法使用Pool組件,除去Java 2 SDK外,還需要先準(zhǔn)備下列一些東西:

Jakarta Commons Pool

所需版本:1.0.1+
下載地址: http://jakarta.apache.org/commons/pool
作用:處理對(duì)象池化
Jakarta Commons Collections

所需版本:2.1+
下載地址: http://jakarta.apache.org/commons/collections
作用:支持Jakarta Commons Pool的運(yùn)行
以上兩種軟件均有已編譯包和源代碼包兩種形式可供選擇。一般情況下,使用已編譯包即可。不過(guò)建議同時(shí)也下載源代碼包,作為參考資料使用。

如果打算使用源代碼包自行編譯,那么還需要準(zhǔn)備以下一些東西:

Ant

所需版本:1.5.3+
下載地址: http://ant.apache.org
作用:運(yùn)行編譯用腳本
JUnit

所需版本:3.8.1+
下載地址: http://www.junit.org
作用:編譯和運(yùn)行單元測(cè)試
具體的編譯方法,可以參看有關(guān)的Ant文檔。

將解壓或編譯后得到的commons-pool.jar和commons-collections.jar放入CLASSPATH,就可以開(kāi)始使用Pool組件了。

PoolableObjectFactory、ObjectPool和ObjectPoolFactory
在Pool組件中,對(duì)象池化的工作被劃分給了三類對(duì)象:

PoolableObjectFactory用于管理被池化的對(duì)象的產(chǎn)生、激活、掛起、校驗(yàn)和銷毀;


ObjectPool用于管理要被池化的對(duì)象的借出和歸還,并通知PoolableObjectFactory完成相應(yīng)的工作;


ObjectPoolFactory則用于大量生成相同類型和設(shè)置的ObjectPool。


相應(yīng)地,使用Pool組件的過(guò)程,也大體可以劃分成“創(chuàng)立PoolableObjectFactory”、“使用ObjectPool”和可選的“利用ObjectPoolFactory”三種動(dòng)作。

創(chuàng)立PoolableObjectFactory
Pool組件利用PoolableObjectFactory來(lái)照看被池化的對(duì)象。ObjectPool的實(shí)例在需要處理被池化的對(duì)象的產(chǎn)生、激活、掛起、校驗(yàn)和銷毀工作時(shí),就會(huì)調(diào)用跟它關(guān)聯(lián)在一起的PoolableObjectFactory實(shí)例的相應(yīng)方法來(lái)操作。

PoolableObjectFactory 是在org.apache.commons.pool包中定義的一個(gè)接口。實(shí)際使用的時(shí)候需要利用這個(gè)接口的一個(gè)具體實(shí)現(xiàn)。Pool組件本身沒(méi)有包含任何一種PoolableObjectFactory實(shí)現(xiàn),需要根據(jù)情況自行創(chuàng)立。

創(chuàng)立PoolableObjectFactory的大體步驟是:

創(chuàng)建一個(gè)實(shí)現(xiàn)了PoolableObjectFactory接口的類。


import org.apache.commons.pool.PoolableObjectFactory;

public class PoolableObjectFactorySample
implements PoolableObjectFactory {
private static int counter = 0;
}




為這個(gè)類添加一個(gè)Object makeObject()方法。這個(gè)方法用于在必要時(shí)產(chǎn)生新的對(duì)象。


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





為這個(gè)類添加一個(gè)void activateObject(Object obj)方法。這個(gè)方法用于將對(duì)象“激活”??設(shè)置為適合開(kāi)始使用的狀態(tài)。

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





為這個(gè)類添加一個(gè)void passivateObject(Object obj)方法。這個(gè)方法用于將對(duì)象“掛起”??設(shè)置為適合開(kāi)始休眠的狀態(tài)。

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





為這個(gè)類添加一個(gè)boolean validateObject(Object obj)方法。這個(gè)方法用于校驗(yàn)一個(gè)具體的對(duì)象是否仍然有效,已失效的對(duì)象會(huì)被自動(dòng)交給destroyObject方法銷毀

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




為這個(gè)類添加一個(gè)void destroyObject(Object obj)方法。這個(gè)方法用于銷毀被validateObject判定為已失效的對(duì)象。

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




最后完成的PoolableObjectFactory類似這個(gè)樣子:


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的概率將對(duì)象判定為失效 */
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之后,便可以開(kāi)始請(qǐng)出ObjectPool來(lái)與之同臺(tái)演出了。

ObjectPool 是在org.apache.commons.pool包中定義的一個(gè)接口,實(shí)際使用的時(shí)候也需要利用這個(gè)接口的一個(gè)具體實(shí)現(xiàn)。Pool組件本身包含了若干種現(xiàn)成的ObjectPool實(shí)現(xiàn),可以直接利用。如果都不合用,也可以根據(jù)情況自行創(chuàng)建。具體的創(chuàng)建方法,可以參看Pool組件的文檔和源碼。

ObjectPool的使用方法類似這樣:

生成一個(gè)要用的PoolableObjectFactory類的實(shí)例。

PoolableObjectFactory factory = new PoolableObjectFactorySample();




利用這個(gè)PoolableObjectFactory實(shí)例為參數(shù),生成一個(gè)實(shí)現(xiàn)了ObjectPool接口的類(例如StackObjectPool)的實(shí)例,作為對(duì)象池。

ObjectPool pool = new StackObjectPool(factory);




需要從對(duì)象池中取出對(duì)象時(shí),調(diào)用該對(duì)象池的Object borrowObject()方法。

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




需要將對(duì)象放回對(duì)象池中時(shí),調(diào)用該對(duì)象池的void returnObject(Object obj)方法。

pool.returnObject(obj);




當(dāng)不再需要使用一個(gè)對(duì)象池時(shí),調(diào)用該對(duì)象池的void close()方法,釋放它所占據(jù)的資源。

pool.close();




這些操作都可能會(huì)拋出異常,需要另外處理。

比較完整的使用ObjectPool的全過(guò)程,可以參考這段代碼:


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;//明確地設(shè)為null,作為對(duì)象已歸還的標(biāo)志
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try{
if (obj != null) {//避免將一個(gè)對(duì)象歸還兩次
pool.returnObject(obj);
}
pool.close();
}
catch (Exception e){
e.printStackTrace();
}
}
}
}




另外,ObjectPool接口還定義了幾個(gè)可以由具體的實(shí)現(xiàn)決定要不要支持的操作,包括:

void clear()

清除所有當(dāng)前在此對(duì)象池中休眠的對(duì)象。

int getNumActive()

返回已經(jīng)從此對(duì)象池中借出的對(duì)象的總數(shù)。

int getNumIdle()

返回當(dāng)前在此對(duì)象池中休眠的對(duì)象的數(shù)目。

void setFactory(PoolableObjectFactory factory)

將當(dāng)前對(duì)象池與參數(shù)中給定的PoolableObjectFactory相關(guān)聯(lián)。如果在當(dāng)前狀態(tài)下,無(wú)法完成這一操作,會(huì)有一個(gè)IllegalStateException異常拋出。

如果所用的ObjectPool實(shí)現(xiàn)不支持這些操作,那么調(diào)用這些方法的時(shí)候,會(huì)拋出一個(gè)UnsupportedOperationException異常。
利用ObjectPoolFactory
有時(shí)候,要在多處生成類型和設(shè)置都相同的ObjectPool。如果在每個(gè)地方都重寫(xiě)一次調(diào)用相應(yīng)構(gòu)造方法的代碼,不但比較麻煩,而且日后修改起來(lái),也有所不便。這種時(shí)候,正是使用ObjectPoolFactory的時(shí)機(jī)。

ObjectPoolFactory是一個(gè)在org.apache.commons.pool中定義的接口,它定義了一個(gè)稱為ObjectPool createPool()方法,可以用于大量生產(chǎn)類型和設(shè)置都相同的ObjectPool。

Pool 組件中,對(duì)每一個(gè)ObjectPool實(shí)現(xiàn),都有一個(gè)對(duì)應(yīng)的ObjectPoolFactory實(shí)現(xiàn)。它們相互之間,有一一對(duì)應(yīng)的參數(shù)相同的構(gòu)造方法。使用的時(shí)候,只要先用想要的參數(shù)和想用的ObjectPoolFactory實(shí)例,構(gòu)造出一個(gè)ObjectPoolFactory對(duì)象,然后在需要生成 ObjectPool的地方,調(diào)用這個(gè)對(duì)象的createPool()方法就可以了。日后無(wú)論想要調(diào)整所用ObjectPool的參數(shù)還是類型,只需要修改這一處,就可以大功告成了。

將《使用ObjectPool》一節(jié)中的例子,改為使用ObjectPoolFactory來(lái)生成所用的ObjectPool對(duì)象之后,基本就是這種形式:


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();
}
}
}
}

使用Jakarta?Commons?Pool處理對(duì)象池化(二)(zt)

(2007-04-24 14:54:38) 轉(zhuǎn)載▼
??

借助BasePoolableObjectFactory
PoolableObjectFactory 定義了許多方法,可以適應(yīng)多種不同的情況。但是,在并沒(méi)有什么特殊需要的時(shí)候,直接實(shí)現(xiàn)PoolableObjectFactory接口,就要編寫(xiě)若干的不進(jìn)行任何操作,或是始終返回true的方法來(lái)讓編譯通過(guò),比較繁瑣。這種時(shí)候就可以借助BasePoolableObjectFactory的威力,來(lái)簡(jiǎn)化編碼的工作。

BasePoolableObjectFactory是org.apache.commons.pool包中的一個(gè)抽象類。它實(shí)現(xiàn)了PoolableObjectFactory接口,并且為除了makeObject之外的方法提供了一個(gè)基本的實(shí)現(xiàn)?? activateObject、passivateObject和destroyObject不進(jìn)行任何操作,而validateObject始終返回 true。通過(guò)繼承這個(gè)類,而不是直接實(shí)現(xiàn)PoolableObjectFactory接口,就可以免去編寫(xiě)一些只起到讓編譯通過(guò)的作用的代碼的麻煩了。

這個(gè)例子展示了一個(gè)從BasePoolableObjectFactory擴(kuò)展而來(lái)的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
可口可樂(lè)公司的軟飲料有可口可樂(lè)、雪碧和芬達(dá)等品種,百事可樂(lè)公司的軟飲料有百事可樂(lè)、七喜和美年達(dá)等類型,而Pool組件提供的ObjectPool實(shí)現(xiàn)則有StackObjectPool、SoftReferenceObjectPool和GenericObjectPool等種類。

不同類型的軟飲料各有各自的特點(diǎn),分別適應(yīng)不同消費(fèi)者的口味;而不同類型的ObjectPool也各有各自的特色,分別適應(yīng)不同的情況。

StackObjectPool
StackObjectPool利用一個(gè)java.util.Stack對(duì)象來(lái)保存對(duì)象池里的對(duì)象。這種對(duì)象池的特色是:

可以為對(duì)象池指定一個(gè)初始的參考大小(當(dāng)空間不夠時(shí)會(huì)自動(dòng)增長(zhǎng))。
在對(duì)象池已空的時(shí)候,調(diào)用它的borrowObject方法,會(huì)自動(dòng)返回新創(chuàng)建的實(shí)例。
可以為對(duì)象池指定一個(gè)可保存的對(duì)象數(shù)目的上限。達(dá)到這個(gè)上限之后,再向池里送回的對(duì)象會(huì)被自動(dòng)送去回收。
StackObjectPool的構(gòu)造方法共有六個(gè),其中:

最簡(jiǎn)單的一個(gè)是StackObjectPool(),一切采用默認(rèn)的設(shè)置,也不指明要用的PoolableObjectFactory實(shí)例。
最復(fù)雜的一個(gè)則是StackObjectPool(PoolableObjectFactory factory, int max, int init)。其中:

參數(shù)factory指明要與之配合使用的PoolableObjectFactory實(shí)例;
參數(shù)max設(shè)定可保存對(duì)象數(shù)目的上限;
參數(shù)init則指明初始的參考大小。
剩余的四個(gè)構(gòu)造方法則是最復(fù)雜的構(gòu)造方法在某方面的簡(jiǎn)化版本,可以根據(jù)需要選用。它們是:

StackObjectPool(int max)
StackObjectPool(int max, int init)
StackObjectPool(PoolableObjectFactory factory)
StackObjectPool(PoolableObjectFactory factory, int max)
用不帶factory參數(shù)的構(gòu)造方法構(gòu)造的StackObjectPool實(shí)例,必須要在用它的setFactory(PoolableObjectFactory factory)方法與某一PoolableObjectFactory實(shí)例關(guān)聯(lián)起來(lái)后才能正常使用。

這種對(duì)象池可以在沒(méi)有Jakarta Commmons Collections組件支持的情況下正常運(yùn)行。

SoftReferenceObjectPool
SoftReferenceObjectPool利用一個(gè)java.util.ArrayList對(duì)象來(lái)保存對(duì)象池里的對(duì)象。不過(guò)它并不在對(duì)象池里直接保存對(duì)象本身,而是保存它們的“軟引用”(Soft Reference)。這種對(duì)象池的特色是:

可以保存任意多個(gè)對(duì)象,不會(huì)有容量已滿的情況發(fā)生。
在對(duì)象池已空的時(shí)候,調(diào)用它的borrowObject方法,會(huì)自動(dòng)返回新創(chuàng)建的實(shí)例。
可以在初始化同時(shí),在池內(nèi)預(yù)先創(chuàng)建一定量的對(duì)象。
當(dāng)內(nèi)存不足的時(shí)候,池中的對(duì)象可以被Java虛擬機(jī)回收。
SoftReferenceObjectPool的構(gòu)造方法共有三個(gè),其中:

最簡(jiǎn)單的是SoftReferenceObjectPool(),不預(yù)先在池內(nèi)創(chuàng)建對(duì)象,也不指明要用的PoolableObjectFactory實(shí)例。
最復(fù)雜的一個(gè)則是SoftReferenceObjectPool(PoolableObjectFactory factory, int initSize)。其中:

參數(shù)factory指明要與之配合使用的PoolableObjectFactory實(shí)例
參數(shù)initSize則指明初始化時(shí)在池中創(chuàng)建多少個(gè)對(duì)象。
剩下的一個(gè)構(gòu)造方法,則是最復(fù)雜的構(gòu)造方法在某方面的簡(jiǎn)化版本,適合在大多數(shù)情況下使用。它是:

SoftReferenceObjectPool(PoolableObjectFactory factory)
用不帶factory參數(shù)的構(gòu)造方法構(gòu)造的SoftReferenceObjectPool實(shí)例,也要在用它的setFactory (PoolableObjectFactory factory)方法與某一PoolableObjectFactory實(shí)例關(guān)聯(lián)起來(lái)后才能正常使用。

這種對(duì)象池也可以在沒(méi)有Jakarta Commmons Collections組件支持的情況下正常運(yùn)行。

GenericObjectPool
GenericObjectPool利用一個(gè)org.apache.commons.collections.CursorableLinkedList對(duì)象來(lái)保存對(duì)象池里的對(duì)象。這種對(duì)象池的特色是:

可以設(shè)定最多能從池中借出多少個(gè)對(duì)象。
可以設(shè)定池中最多能保存多少個(gè)對(duì)象。
可以設(shè)定在池中已無(wú)對(duì)象可借的情況下,調(diào)用它的borrowObject方法時(shí)的行為,是等待、創(chuàng)建新的實(shí)例還是拋出異常。
可以分別設(shè)定對(duì)象借出和還回時(shí),是否進(jìn)行有效性檢查。
可以設(shè)定是否使用一個(gè)單獨(dú)的線程,對(duì)池內(nèi)對(duì)象進(jìn)行后臺(tái)清理。
GenericObjectPool的構(gòu)造方法共有七個(gè),其中:

最簡(jiǎn)單的一個(gè)是GenericObjectPool(PoolableObjectFactory factory)。僅僅指明要用的PoolableObjectFactory實(shí)例,其它參數(shù)則采用默認(rèn)值。
最 復(fù)雜的一個(gè)是GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle)。其中:

參數(shù)factory指明要與之配合使用的PoolableObjectFactory實(shí)例。
參數(shù)maxActive指明能從池中借出的對(duì)象的最大數(shù)目。如果這個(gè)值不是正數(shù),表示沒(méi)有限制。
參數(shù)whenExhaustedAction指定在池中借出對(duì)象的數(shù)目已達(dá)極限的情況下,調(diào)用它的borrowObject方法時(shí)的行為。可以選用的值有:
GenericObjectPool.WHEN_EXHAUSTED_BLOCK,表示等待;
GenericObjectPool.WHEN_EXHAUSTED_GROW,表示創(chuàng)建新的實(shí)例(不過(guò)這就使maxActive參數(shù)失去了意義);
GenericObjectPool.WHEN_EXHAUSTED_FAIL,表示拋出一個(gè)java.util.NoSuchElementException異常。
參數(shù)maxWait指明若在對(duì)象池空時(shí)調(diào)用borrowObject方法的行為被設(shè)定成等待,最多等待多少毫秒。如果等待時(shí)間超過(guò)了這個(gè)數(shù)值,則會(huì)拋出一個(gè)java.util.NoSuchElementException異常。如果這個(gè)值不是正數(shù),表示無(wú)限期等待。
參數(shù)testOnBorrow設(shè)定在借出對(duì)象時(shí)是否進(jìn)行有效性檢查。
參數(shù)testOnBorrow設(shè)定在還回對(duì)象時(shí)是否進(jìn)行有效性檢查。
參數(shù)timeBetweenEvictionRunsMillis,設(shè)定間隔每過(guò)多少毫秒進(jìn)行一次后臺(tái)對(duì)象清理的行動(dòng)。如果這個(gè)值不是正數(shù),則實(shí)際上不會(huì)進(jìn)行后臺(tái)對(duì)象清理。
參數(shù)numTestsPerEvictionRun,設(shè)定在進(jìn)行后臺(tái)對(duì)象清理時(shí),每次檢查幾個(gè)對(duì)象。如果這個(gè)值不是正數(shù),則每次檢查的對(duì)象數(shù)是檢查時(shí)池內(nèi)對(duì)象的總數(shù)乘以這個(gè)值的負(fù)倒數(shù)再向上取整的結(jié)果??也就是說(shuō),如果這個(gè)值是-2(-3、-4、-5……)的話,那么每次大約檢查當(dāng)時(shí)池內(nèi)對(duì)象總數(shù)的1/2 (1/3、1/4、1/5……)左右。
參數(shù)minEvictableIdleTimeMillis,設(shè)定在進(jìn)行后臺(tái)對(duì)象清理時(shí),視休眠時(shí)間超過(guò)了多少毫秒的對(duì)象為過(guò)期。過(guò)期的對(duì)象將被回收。如果這個(gè)值不是正數(shù),那么對(duì)休眠時(shí)間沒(méi)有特別的約束。
參數(shù)testWhileIdle,則設(shè)定在進(jìn)行后臺(tái)對(duì)象清理時(shí),是否還對(duì)沒(méi)有過(guò)期的池內(nèi)對(duì)象進(jìn)行有效性檢查。不能通過(guò)有效性檢查的對(duì)象也將被回收。
另一個(gè)比較特別的構(gòu)造方法是GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) 。其中:

參數(shù)factory指明要與之配合使用的PoolableObjectFactory實(shí)例;
參數(shù)config則指明一個(gè)包括了各個(gè)參數(shù)的預(yù)設(shè)值的對(duì)象(詳見(jiàn)《GenericObjectPool.Config》一節(jié))。
剩下的五個(gè)構(gòu)造函數(shù)則是最復(fù)雜的構(gòu)造方法在某方面的簡(jiǎn)化版本,可以根據(jù)情況選用。它們是:

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)
這種對(duì)象池不可以在沒(méi)有Jakarta Commmons Collections組件支持的情況下運(yùn)行。

GenericObjectPool.Config
調(diào)用一個(gè)有很多的參數(shù)的方法的時(shí)候,很可能將參數(shù)的位置和個(gè)數(shù)搞錯(cuò),導(dǎo)致編譯或運(yùn)行時(shí)的錯(cuò)誤;閱讀包含了有很多參數(shù)的方法調(diào)用的代碼的時(shí)候,也很可能因?yàn)闆](méi)有搞對(duì)參數(shù)的位置和個(gè)數(shù),產(chǎn)生錯(cuò)誤的理解。因此,人們往往避免給一個(gè)方法安排太多的參數(shù)的做法(所謂的“Long Parameter List”)。不過(guò),有些方法又確實(shí)需要許多參數(shù)才能完成工作。于是,就有人想到了一種將大批的參數(shù)封裝到一個(gè)對(duì)象(稱為參數(shù)對(duì)象,Parameter Object)里,然后將這個(gè)對(duì)象作為單一的參數(shù)傳遞的兩全其美的對(duì)策。

因?yàn)樯蒅enericKeyedObjectPool時(shí)可供設(shè)置的特性非常之多,所以它的構(gòu)造方法里也就難免會(huì)需要不少的參數(shù)。GenericKeyedObjectPool除了提供了幾個(gè)超長(zhǎng)的構(gòu)造方法之外,同時(shí)也定義了一個(gè)使用參數(shù)對(duì)象的構(gòu)造方法。所用參數(shù)對(duì)象的類型是GenericKeyedObjectPool.Config。

GenericKeyedObjectPool.Config定義了許多的public字段,每個(gè)對(duì)應(yīng)一種可以為GenericKeyedObjectPool設(shè)置的特性,包括:

int maxActive
int maxIdle
long maxWait
long minEvictableIdleTimeMillis
int numTestsPerEvictionRun
boolean testOnBorrow
boolean testOnReturn
boolean testWhileIdle
long timeBetweenEvictionRunsMillis
byte whenExhaustedAction
這些字段的作用,與在GenericKeyedObjectPool最復(fù)雜的構(gòu)造方法中與它們同名的參數(shù)完全相同。

使用的時(shí)候,先生成一個(gè)GenericKeyedObjectPool.Config對(duì)象,然后將個(gè)字段設(shè)置為想要的值,最后用這個(gè)對(duì)象作為唯一的參數(shù)調(diào)用GenericKeyedObjectPool的構(gòu)造方法即可。

注意:使用有許多public字段、卻沒(méi)有任何方法的類,也是一個(gè)人們往往加以避免的行為(所謂的“Data Class”)。不過(guò)這次GenericKeyedObjectPool特立獨(dú)行了一回。

帶鍵值的對(duì)象池
有時(shí)候,單用對(duì)池內(nèi)所有對(duì)象一視同仁的對(duì)象池,并不能解決的問(wèn)題。例如,對(duì)于一組某些參數(shù)設(shè)置不同的同類對(duì)象??比如一堆指向不同地址的 java.net.URL對(duì)象或者一批代表不同語(yǔ)句的java.sql.PreparedStatement對(duì)象,用這樣的方法池化,就有可能取出不合用的對(duì)象的麻煩。

可以通過(guò)為每一組參數(shù)相同的同類對(duì)象建立一個(gè)單獨(dú)的對(duì)象池來(lái)解決這個(gè)問(wèn)題。但是,如果使用普通的ObjectPool 來(lái)實(shí)施這個(gè)計(jì)策的話,因?yàn)槠胀ǖ腜oolableObjectFactory只能生產(chǎn)出大批設(shè)置完全一致的對(duì)象,就需要為每一組參數(shù)相同的對(duì)象編寫(xiě)一個(gè)單獨(dú)的PoolableObjectFactory,工作量相當(dāng)可觀。這種時(shí)候就適合調(diào)遣Pool組件中提供的一種“帶鍵值的對(duì)象池”來(lái)展開(kāi)工作了。

Pool 組件采用實(shí)現(xiàn)了KeyedObjectPool接口的類,來(lái)充當(dāng)帶鍵值的對(duì)象池。相應(yīng)的,這種對(duì)象池需要配合實(shí)現(xiàn)了 KeyedPoolableObjectFactory接口的類和實(shí)現(xiàn)了KeyedObjectPoolFactory接口的類來(lái)使用(這三個(gè)接口都在 org.apache.commons.pool包中定義):

KeyedPoolableObjectFactory和PoolableObjectFactory形式如出一轍,只是每個(gè)方法都增加了一個(gè)Object key參數(shù)而已:

makeObject的參數(shù)變?yōu)?Object key)
activateObject的參數(shù)變?yōu)?Object key, Object obj)
passivateObject的參數(shù)變?yōu)?Object key, Object obj)
validateObject的參數(shù)變?yōu)镺bject key, Object obj)
destroyObject的參數(shù)變?yōu)?Object key, Object obj)
另外Pool組件也提供了BaseKeyedPoolableObjectFactory,用于充當(dāng)和BasePoolableObjectFactory差不多的角色。

KeyedObjectPool和ObjectPool的形式大同小異,只是某些方法的參數(shù)類型發(fā)生了變化,某些方法分成了兩種略有不同的版本:

用Object borrowObject(Object key)和void returnObject(Object key, Object obj)來(lái)負(fù)責(zé)對(duì)象出借和歸還的動(dòng)作。
用void close()來(lái)關(guān)閉不再需要的對(duì)象池。
用void clear(Object key)和void clear()來(lái)清空池中的對(duì)象,前者針對(duì)與特定鍵值相關(guān)聯(lián)的實(shí)例,后者針對(duì)整個(gè)對(duì)象池。
用int getNumActive(Object key)和int getNumActive()來(lái)查詢已借出的對(duì)象數(shù),前者針對(duì)與特定鍵值相關(guān)聯(lián)的實(shí)例,后者針對(duì)整個(gè)對(duì)象池。
用int getNumIdle(Object key)和int getNumIdle()來(lái)查詢正在休眠的對(duì)象數(shù),前者針對(duì)與特定鍵值相關(guān)聯(lián)的實(shí)例,后者針對(duì)整個(gè)對(duì)象池。
用void setFactory(KeyedPoolableObjectFactory factory)來(lái)設(shè)置要用的KeyedPoolableObjectFactory實(shí)例。
void clear、int getNumActive、int getNumIdle和void setFactory的各種版本都仍然是可以由具體實(shí)現(xiàn)自行決定是否要支持的方法。如果所用的KeyedObjectPool實(shí)現(xiàn)不支持這些操作,那么調(diào)用這些方法的時(shí)候,會(huì)拋出一個(gè)UnsupportedOperationException異常。

KeyedObjectPoolFactory和ObjectPoolFactory的形式完全相同,只是所代表的對(duì)象不同而已。
這一類對(duì)象池的基本使用方法接近于這樣:


KeyedObjectPoolSample.java



import org.apache.commons.pool.BaseKeyedPoolableObjectFactory;
import org.apache.commons.pool.KeyedObjectPool;
import org.apache.commons.pool.KeyedObjectPoolFactory;
import org.apache.commons.pool.KeyedPoolableObjectFactory;
import org.apache.commons.pool.impl.StackKeyedObjectPoolFactory;

class KeyedPoolableObjectFactorySample
extends BaseKeyedPoolableObjectFactory {

public Object makeObject(Object key) throws Exception {
return new String("[" + key.hashCode() + "]");
}

}

public class KeyedObjectPoolSample {
public static void main(String[] args) {
Object obj = null;
KeyedPoolableObjectFactory factory
= new KeyedPoolableObjectFactorySample();
KeyedObjectPoolFactory poolFactory
= new StackKeyedObjectPoolFactory(factory);
KeyedObjectPool pool = poolFactory.createPool();
String key = null;
try {
for (long i = 0; i < 100 ; i++) {
key = "" + (int) (Math.random() * 10);
System.out.println("== " + i + " ==");
System.out.println("Key:" + key);
obj = pool.borrowObject(key);
System.out.println("Object:" + obj);
pool.returnObject(key, obj);
obj = null;
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try{
if (obj != null) {
pool.returnObject(key, obj);
}
pool.close();
}
catch (Exception e){
e.printStackTrace();
}
}
}
}





Pool 組件自帶的KeyedObjectPool的實(shí)現(xiàn)有StackKeyedObjectPool和GenericKeyedObjectPool兩種。它們的使用方法分別與它們各自的近親KeyedObjectPool和KeyedObjectPool基本一致,只是原來(lái)使用 GenericObjectPool.Config的地方要使用GenericKeyedObjectPool.Config代替。

當(dāng)出借少于歸還
Java 并未提供一種機(jī)制來(lái)保證兩個(gè)方法被調(diào)用的次數(shù)之間呈現(xiàn)一種特定的關(guān)系(相等,相差一個(gè)常數(shù),或是其它任何關(guān)系)。因此,完全可以做到建立一個(gè) ObjectPool對(duì)象,然后調(diào)用一次borrowObject方法,借出一個(gè)對(duì)象,之后重復(fù)兩次returnObject方法調(diào)用,進(jìn)行兩次歸還。而調(diào)用一個(gè)從不曾借出對(duì)象的ObjectPool的returnObject方法也并不是一個(gè)不可完成的任務(wù)。

盡管這些使用方法并不合乎returnObject的字面意思,但是Pool組件中的各個(gè)ObjectPool/KeyedObjectPool實(shí)現(xiàn)都不在乎這一點(diǎn)。它們的 returnObject方法都只是單純地召喚與當(dāng)前對(duì)象池關(guān)聯(lián)的PoolableObjectFactory實(shí)例,看這對(duì)象能否經(jīng)受得起 validateObject的考驗(yàn)而已。考驗(yàn)的結(jié)果決定了這個(gè)對(duì)象是應(yīng)該拿去作passivateObject處理,而后留待重用;還是應(yīng)該拿去作 destroyObject處理,以免占用資源。也就是說(shuō),當(dāng)出借少于歸還的時(shí)候,并不會(huì)額外發(fā)生什么特別的事情(當(dāng)然,有可能因?yàn)樵搶?duì)象池處于不接受歸還對(duì)象的請(qǐng)求的狀態(tài)而拋出異常,不過(guò)這是常規(guī)現(xiàn)象)。

在實(shí)際使用中,可以利用這一特性來(lái)向?qū)ο蟪貎?nèi)加入通過(guò)其它方法生成的對(duì)象。

線程安全問(wèn)題
有時(shí)候可能要在多線程環(huán)境下使用Pool組件,這時(shí)候就會(huì)遇到和Pool組件的線程安全程度有關(guān)的問(wèn)題。

因?yàn)镺bjectPool和KeyedObjectPool都是在org.apache.commons.pool中定義的接口,而在接口中無(wú)法使用 “synchronized”來(lái)修飾方法,所以,一個(gè)ObjectPool/KeyedObjectPool下的各個(gè)方法是否是同步方法,完全要看具體的實(shí)現(xiàn)。而且,單純地使用了同步方法,也并不能使對(duì)象就此在多線程環(huán)境里高枕無(wú)憂。

就Pool組件中自帶的幾個(gè)ObjectPool/KeyedObjectPool的實(shí)現(xiàn)而言,它們都在一定程度上考慮了在多線程環(huán)境中使用的情況。不過(guò)還不能說(shuō)它們是完全“線程安全”的。

例如,這段代碼有些時(shí)候就會(huì)有一些奇怪的表現(xiàn),最后輸出的結(jié)果比預(yù)期的要大:


UnsafeMultiThreadPoolingSample.java



import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.StackObjectPool;

class UnsafePicker extends Thread {
private ObjectPool pool;
public UnsafePicker(ObjectPool op) {
pool = op;
}
public void run() {
Object obj = null;
try {
/* 似乎…… */
if ( pool.getNumActive() < 5 ) {
sleep((long) (Math.random() * 10));
obj = pool.borrowObject();
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}

public class UnsafeMultiThreadPoolingSample {

public static void main(String[] args) {
ObjectPool pool = new StackObjectPool
(new BasePoolableObjectFactorySample());
Thread ts[] = new Thread[20];
for (int j = 0; j < ts.length; j++) {
ts[j] = new UnsafePicker(pool);
ts[j].start();
}
try {
Thread.sleep(1000);
/* 然而…… */
System.out.println("NumActive:" + pool.getNumActive());
}
catch (Exception e) {
e.printStackTrace();
}
}
}






要避免這種情況,就要進(jìn)一步采取一些措施才行:


SafeMultiThreadPoolingSample.java



import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.StackObjectPool;

class SafePicker extends Thread {
private ObjectPool pool;
public SafePicker(ObjectPool op) {
pool = op;
}
public void run() {
Object obj = null;
try {
/* 略加處理 */
synchronized (pool) {
if ( pool.getNumActive() < 5 ) {
sleep((long) (Math.random() * 10));
obj = pool.borrowObject();
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}

public class SafeMultiThreadPoolingSample {

public static void main(String[] args) {
ObjectPool pool = new StackObjectPool
(new BasePoolableObjectFactorySample());
Thread ts[] = new Thread[20];
for (int j = 0; j < ts.length; j++) {
ts[j] = new SafePicker(pool);
ts[j].start();
}
try {
Thread.sleep(1000);
System.out.println("NumActive:" + pool.getNumActive());
}
catch (Exception e) {
e.printStackTrace();
}
}
}




基本上,可以說(shuō)Pool組件是線程相容的。但是要在多線程環(huán)境中使用,還需要作一些特別的處理。

什么時(shí)候不要池化
采用對(duì)象池化的本意,是要通過(guò)減少對(duì)象生成的次數(shù),減少花在對(duì)象初始化上面的開(kāi)銷,從而提高整體的性能。然而池化處理本身也要付出代價(jià),因此,并非任何情況下都適合采用對(duì)象池化。

Dr. Cliff Click在JavaOne 2003上發(fā)表的《Performance Myths Exposed》中,給出了一組其它條件都相同時(shí),使用與不使用對(duì)象池化技術(shù)的實(shí)際性能的比較結(jié)果。他的實(shí)測(cè)結(jié)果表明:

對(duì)于類似Point這樣的輕量級(jí)對(duì)象,進(jìn)行池化處理后,性能反而下降,因此不宜池化;

對(duì)于類似Hashtable這樣的中量級(jí)對(duì)象,進(jìn)行池化處理后,性能基本不變,一般不必池化(池化會(huì)使代碼變復(fù)雜,增大維護(hù)的難度);
對(duì)于類似JPanel這樣的重量級(jí)對(duì)象,進(jìn)行池化處理后,性能有所上升,可以考慮池化。
根據(jù)使用方法的不同,實(shí)際的情況可能與這一測(cè)量結(jié)果略有出入。在配置較高的機(jī)器和技術(shù)較強(qiáng)的虛擬機(jī)上,不宜池化的對(duì)象的范圍可能會(huì)更大。不過(guò),對(duì)于像網(wǎng)絡(luò)和數(shù)據(jù)庫(kù)連接這類重量級(jí)的對(duì)象來(lái)說(shuō),目前還是有池化的必要。

基本上,只在重復(fù)生成某種對(duì)象的操作成為影響性能的關(guān)鍵因素的時(shí)候,才適合進(jìn)行對(duì)象池化。如果進(jìn)行池化所能帶來(lái)的性能提高并不重要的話,還是不采用對(duì)象池化技術(shù),以保持代碼的簡(jiǎn)明,而使用更好的硬件和更棒的虛擬機(jī)來(lái)提高性能為佳。

結(jié)束語(yǔ)
恰當(dāng)?shù)厥褂脤?duì)象池化,可以有效地降低頻繁生成某些對(duì)象所造成的開(kāi)銷,從而提高整體的性能。而借助Jakarta Commons Pool組件,可以有效地減少花在處理對(duì)象池化上的工作量,進(jìn)而,向其它重要的工作里,投入更多的時(shí)間和精力。

參考資料

很多疑難問(wèn)題的答案都可以通過(guò)查閱Pool組件的Javadoc文檔和源代碼的方法解決。


從 Pool組件的官方站點(diǎn) 上,還可以進(jìn)一步得到許多有用的信息。


DBCP 是一個(gè)基于Pool組件的Java數(shù)據(jù)庫(kù)連接池管理組件,同時(shí)也可以作為Pool組件的用法示例使用。


蔡學(xué)鏞在 《Java夜未眠(Sleepless Java)》 中的《測(cè)不準(zhǔn)原理》一文里,介紹了Java中的包括“軟引用”(Soft Reference)在內(nèi)的各種不同的引用的特點(diǎn)和用處。


Martin Fowler在 《Refactoring -- Improving the Design of Existing Code》(中譯本名為《重構(gòu)??改善既有代碼的設(shè)計(jì)》,由侯捷、熊節(jié)合譯)一書(shū)的第三章《代碼的壞味道(Bad Smells in Code)》中討論了被稱為“Long Parameter List”和“Data Class”的“壞味道”。并指明了一些可以用于對(duì)付這些問(wèn)題的重構(gòu)手法。


Brian Goetz在IBM developerWorks上發(fā)表的《Java 理論與實(shí)踐:描繪線程安全性》一文中,說(shuō)明了為什么單純地使用同步方法還不能讓對(duì)象就此在多線程環(huán)境里高枕無(wú)憂的原因。


Dr. Cliff Click發(fā)表在JavaOne 2003上的《Performance Myths Exposed》(Session #1522),給出了一組包括“對(duì)象池化”在內(nèi)的、對(duì)“能提高Java程序性能”的做法的實(shí)際效果的測(cè)試數(shù)據(jù),和一些恰當(dāng)使用這些做法的建議。

?

總結(jié)

以上是生活随笔為你收集整理的对象池commons-pool的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。