☆為什么使用對象池
☆對象池思路
☆運用對象池
☆PoolableObjectFactory
☆ObjectPool

☆最終結(jié)構(gòu)

☆簡化PoolableObjectFactory
☆ObjectPool實現(xiàn)類
☆StackObjectPool
☆SoftReferenceObjectPool
☆GenericObjectPool
☆GenericObjectPool.Config
☆帶鍵值的對象池
☆當(dāng)出借少于歸還
☆什么時候使用對象池
恰當(dāng)?shù)厥褂脤ο蟪鼗夹g(shù),可以有效地減少對象生成和初始化時的消耗,提高系統(tǒng)的運行效率。Jakarta Commons Pool組件提供了一整套用于實現(xiàn)對象池化的框架,以及若干種各具特色的對象池實現(xiàn)
☆對象池思路
對象池化的基本思路是:將用過的對象保存起來,等下一次需要這種對象的時候,再拿出來重復(fù)使用,從而在一定程度上減少頻繁創(chuàng)建對象所造成的開銷。用于充當(dāng)保存對象的“容器”的對象,被稱為“對象池”(Object Pool,或簡稱Pool)。
對于沒有狀態(tài)的對象(例如String),在重復(fù)使用之前,無需進行任何處理;對于有狀態(tài)的對象(例如StringBuffer),在重復(fù)使用之前,就需要把它們恢復(fù)到等同于剛剛生成時的狀態(tài)。由于條件的限制,恢復(fù)某個對象的狀態(tài)的操作不可能實現(xiàn)了的話,就得把這個對象拋棄,改用新創(chuàng)建的實例了。
并非所有對象都適合拿來池化――因為維護對象池也要造成一定開銷。對生成時開銷不大的對象進行池化,反而可能會出現(xiàn)“維護對象池的開銷”大于“生成新對象的開銷”,從而使性能降低的情況。但是對于生成時開銷可觀的對象,池化技術(shù)就是提高性能的有效策略了。
對于沒有狀態(tài)的對象(例如String),在重復(fù)使用之前,無需進行任何處理;對于有狀態(tài)的對象(例如StringBuffer),在重復(fù)使用之前,就需要把它們恢復(fù)到等同于剛剛生成時的狀態(tài)。由于條件的限制,恢復(fù)某個對象的狀態(tài)的操作不可能實現(xiàn)了的話,就得把這個對象拋棄,改用新創(chuàng)建的實例了。
并非所有對象都適合拿來池化――因為維護對象池也要造成一定開銷。對生成時開銷不大的對象進行池化,反而可能會出現(xiàn)“維護對象池的開銷”大于“生成新對象的開銷”,從而使性能降低的情況。但是對于生成時開銷可觀的對象,池化技術(shù)就是提高性能的有效策略了。
☆運用對象池
在Pool組件中,對象池化的工作被劃分給了三類對象:
PoolableObjectFactory用于管理被池化的對象的產(chǎn)生、激活、掛起、校驗和銷毀;
ObjectPool用于管理要被池化的對象的借出和歸還,并通知PoolableObjectFactory完成相應(yīng)的工作;
ObjectPoolFactory則用于大量生成相同類型和設(shè)置的ObjectPool。
相應(yīng)地,使用Pool組件的過程,也大體可以劃分成“創(chuàng)立PoolableObjectFactory”、“使用ObjectPool”和可選的“利用ObjectPoolFactory”三種動作。
PoolableObjectFactory用于管理被池化的對象的產(chǎn)生、激活、掛起、校驗和銷毀;
ObjectPool用于管理要被池化的對象的借出和歸還,并通知PoolableObjectFactory完成相應(yīng)的工作;
ObjectPoolFactory則用于大量生成相同類型和設(shè)置的ObjectPool。
相應(yīng)地,使用Pool組件的過程,也大體可以劃分成“創(chuàng)立PoolableObjectFactory”、“使用ObjectPool”和可選的“利用ObjectPoolFactory”三種動作。
☆PoolableObjectFactory

☆ObjectPool

☆最終結(jié)構(gòu)

☆簡化PoolableObjectFactory
PoolableObjectFactory定義了許多方法,可以適應(yīng)多種不同的情況。但是,在并沒有什么特殊需要的時候,直接實現(xiàn)PoolableObjectFactory接口,就要編寫若干的不進行任何操作,或是始終返回true的方法來讓編譯通過,比較繁瑣。這種時候就可以借助BasePoolableObjectFactory的威力,來簡化編碼的工作。
BasePoolableObjectFactory是org.apache.commons.pool包中的一個抽象類。它實現(xiàn)了PoolableObjectFactory接口,并且為除了makeObject之外的方法提供了一個基本的實現(xiàn)――activateObject、passivateObject和destroyObject不進行任何操作,而validateObject始終返回true。通過繼承這個類,而不是直接實現(xiàn)PoolableObjectFactory接口,就可以免去編寫一些只起到讓編譯通過的作用的代碼的麻煩了。
BasePoolableObjectFactory是org.apache.commons.pool包中的一個抽象類。它實現(xiàn)了PoolableObjectFactory接口,并且為除了makeObject之外的方法提供了一個基本的實現(xiàn)――activateObject、passivateObject和destroyObject不進行任何操作,而validateObject始終返回true。通過繼承這個類,而不是直接實現(xiàn)PoolableObjectFactory接口,就可以免去編寫一些只起到讓編譯通過的作用的代碼的麻煩了。
☆ObjectPool實現(xiàn)類
☆StackObjectPool
StackObjectPool利用一個java.util.Stack對象來保存對象池里的對象。這種對象池的特色是:
可以為對象池指定一個初始的參考大?。ó?dāng)空間不夠時會自動增長)。
在對象池已空的時候,調(diào)用它的borrowObject方法,會自動返回新創(chuàng)建的實例。
可以為對象池指定一個可保存的對象數(shù)目的上限。達到這個上限之后,再向池里送回的對象會被自動送去回收。
StackObjectPool的構(gòu)造方法共有六個,其中:
最簡單的一個是StackObjectPool(),一切采用默認的設(shè)置,也不指明要用的PoolableObjectFactory實例。
最復(fù)雜的一個則是StackObjectPool(PoolableObjectFactory factory, int max, int init)。其中:
參數(shù)factory指明要與之配合使用的PoolableObjectFactory實例;
參數(shù)max設(shè)定可保存對象數(shù)目的上限;
參數(shù)init則指明初始的參考大小。
剩余的四個構(gòu)造方法則是最復(fù)雜的構(gòu)造方法在某方面的簡化版本,可以根據(jù)需要選用。它們是:
StackObjectPool(int max)
StackObjectPool(int max, int init)
StackObjectPool(PoolableObjectFactory factory)
StackObjectPool(PoolableObjectFactory factory, int max)
用不帶factory參數(shù)的構(gòu)造方法構(gòu)造的StackObjectPool實例,必須要在用它的setFactory(PoolableObjectFactory factory)方法與某一 PoolableObjectFactory實例關(guān)聯(lián)起來后才能正常使用。
這種對象池可以在沒有Jakarta Commmons Collections組件支持的情況下正常運行。
可以為對象池指定一個初始的參考大?。ó?dāng)空間不夠時會自動增長)。
在對象池已空的時候,調(diào)用它的borrowObject方法,會自動返回新創(chuàng)建的實例。
可以為對象池指定一個可保存的對象數(shù)目的上限。達到這個上限之后,再向池里送回的對象會被自動送去回收。
StackObjectPool的構(gòu)造方法共有六個,其中:
最簡單的一個是StackObjectPool(),一切采用默認的設(shè)置,也不指明要用的PoolableObjectFactory實例。
最復(fù)雜的一個則是StackObjectPool(PoolableObjectFactory factory, int max, int init)。其中:
參數(shù)factory指明要與之配合使用的PoolableObjectFactory實例;
參數(shù)max設(shè)定可保存對象數(shù)目的上限;
參數(shù)init則指明初始的參考大小。
剩余的四個構(gòu)造方法則是最復(fù)雜的構(gòu)造方法在某方面的簡化版本,可以根據(jù)需要選用。它們是:
StackObjectPool(int max)
StackObjectPool(int max, int init)
StackObjectPool(PoolableObjectFactory factory)
StackObjectPool(PoolableObjectFactory factory, int max)
用不帶factory參數(shù)的構(gòu)造方法構(gòu)造的StackObjectPool實例,必須要在用它的setFactory(PoolableObjectFactory factory)方法與某一 PoolableObjectFactory實例關(guān)聯(lián)起來后才能正常使用。
這種對象池可以在沒有Jakarta Commmons Collections組件支持的情況下正常運行。
☆SoftReferenceObjectPool
SoftReferenceObjectPool利用一個java.util.ArrayList對象來保存對象池里的對象。不過它并不在對象池里直接保存對象本身,而是保存它們的“軟引用”(Soft Reference)。這種對象池的特色是:
可以保存任意多個對象,不會有容量已滿的情況發(fā)生。
在對象池已空的時候,調(diào)用它的borrowObject方法,會自動返回新創(chuàng)建的實例。
可以在初始化同時,在池內(nèi)預(yù)先創(chuàng)建一定量的對象。
當(dāng)內(nèi)存不足的時候,池中的對象可以被Java虛擬機回收。
SoftReferenceObjectPool的構(gòu)造方法共有三個,其中:
最簡單的是SoftReferenceObjectPool(),不預(yù)先在池內(nèi)創(chuàng)建對象,也不指明要用的PoolableObjectFactory實例。
最復(fù)雜的一個則是SoftReferenceObjectPool(PoolableObjectFactory factory, int initSize)。其中:
參數(shù)factory指明要與之配合使用的PoolableObjectFactory實例
參數(shù)initSize則指明初始化時在池中創(chuàng)建多少個對象。
剩下的一個構(gòu)造方法,則是最復(fù)雜的構(gòu)造方法在某方面的簡化版本,適合在大多數(shù)情況下使用。它是:
SoftReferenceObjectPool(PoolableObjectFactory factory)
用不帶factory參數(shù)的構(gòu)造方法構(gòu)造的SoftReferenceObjectPool實例,也要在用它的setFactory(PoolableObjectFactory factory)方法與某一PoolableObjectFactory實例關(guān)聯(lián)起來后才能正常使用。
這種對象池也可以在沒有Jakarta Commmons Collections組件支持的情況下正常運行。
可以保存任意多個對象,不會有容量已滿的情況發(fā)生。
在對象池已空的時候,調(diào)用它的borrowObject方法,會自動返回新創(chuàng)建的實例。
可以在初始化同時,在池內(nèi)預(yù)先創(chuàng)建一定量的對象。
當(dāng)內(nèi)存不足的時候,池中的對象可以被Java虛擬機回收。
SoftReferenceObjectPool的構(gòu)造方法共有三個,其中:
最簡單的是SoftReferenceObjectPool(),不預(yù)先在池內(nèi)創(chuàng)建對象,也不指明要用的PoolableObjectFactory實例。
最復(fù)雜的一個則是SoftReferenceObjectPool(PoolableObjectFactory factory, int initSize)。其中:
參數(shù)factory指明要與之配合使用的PoolableObjectFactory實例
參數(shù)initSize則指明初始化時在池中創(chuàng)建多少個對象。
剩下的一個構(gòu)造方法,則是最復(fù)雜的構(gòu)造方法在某方面的簡化版本,適合在大多數(shù)情況下使用。它是:
SoftReferenceObjectPool(PoolableObjectFactory factory)
用不帶factory參數(shù)的構(gòu)造方法構(gòu)造的SoftReferenceObjectPool實例,也要在用它的setFactory(PoolableObjectFactory factory)方法與某一PoolableObjectFactory實例關(guān)聯(lián)起來后才能正常使用。
這種對象池也可以在沒有Jakarta Commmons Collections組件支持的情況下正常運行。
☆GenericObjectPool
GenericObjectPool利用一個org.apache.commons.collections.CursorableLinkedList對象來保存對象池里的對象。這種對象池的特色是:
可以設(shè)定最多能從池中借出多少個對象。
可以設(shè)定池中最多能保存多少個對象。
可以設(shè)定在池中已無對象可借的情況下,調(diào)用它的borrowObject方法時的行為,是等待、創(chuàng)建新的實例還是拋出異常。
可以分別設(shè)定對象借出和還回時,是否進行有效性檢查。
可以設(shè)定是否使用一個單獨的線程,對池內(nèi)對象進行后臺清理。
GenericObjectPool的構(gòu)造方法共有七個,其中:
最簡單的一個是GenericObjectPool(PoolableObjectFactory factory)。僅僅指明要用的PoolableObjectFactory實例,其它參數(shù)則采用默認值。
最復(fù)雜的一個是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ù)maxActive指明能從池中借出的對象的最大數(shù)目。如果這個值不是正數(shù),表示沒有限制。
參數(shù)whenExhaustedAction指定在池中借出對象的數(shù)目已達極限的情況下,調(diào)用它的borrowObject方法時的行為。可以選用的值有:
GenericObjectPool.WHEN_EXHAUSTED_BLOCK,表示等待;
GenericObjectPool.WHEN_EXHAUSTED_GROW,表示創(chuàng)建新的實例(不過這就使maxActive參數(shù)失去了意義);
GenericObjectPool.WHEN_EXHAUSTED_FAIL,表示拋出一個java.util.NoSuchElementException異常。
參數(shù)maxWait指明若在對象池空時調(diào)用borrowObject方法的行為被設(shè)定成等待,最多等待多少毫秒。如果等待時間超過了這個數(shù)值,則會拋出一個java.util.NoSuchElementException異常。如果這個值不是正數(shù),表示無限期等待。
參數(shù)testOnBorrow設(shè)定在借出對象時是否進行有效性檢查。
參數(shù)testOnBorrow設(shè)定在還回對象時是否進行有效性檢查。
參數(shù)timeBetweenEvictionRunsMillis,設(shè)定間隔每過多少毫秒進行一次后臺對象清理的行動。如果這個值不是正數(shù),則實際上不會進行后臺對象清理。
參數(shù)numTestsPerEvictionRun,設(shè)定在進行后臺對象清理時,每次檢查幾個對象。如果這個值不是正數(shù),則每次檢查的對象數(shù)是檢查時池內(nèi)對象的總數(shù)乘以這個值的負倒數(shù)再向上取整的結(jié)果――也就是說,如果這個值是-2(-3、-4、-5……)的話,那么每次大約檢查當(dāng)時池內(nèi)對象總數(shù)的1/2(1/3、1/4、1/5……)左右。
參數(shù)minEvictableIdleTimeMillis,設(shè)定在進行后臺對象清理時,視休眠時間超過了多少毫秒的對象為過期。過期的對象將被回收。如果這個值不是正數(shù),那么對休眠時間沒有特別的約束。
參數(shù)testWhileIdle,則設(shè)定在進行后臺對象清理時,是否還對沒有過期的池內(nèi)對象進行有效性檢查。不能通過有效性檢查的對象也將被回收。
另一個比較特別的構(gòu)造方法是GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) 。其中:
參數(shù)factory指明要與之配合使用的PoolableObjectFactory實例;
參數(shù)config則指明一個包括了各個參數(shù)的預(yù)設(shè)值的對象(詳見《GenericObjectPool.Config》一節(jié))。
剩下的五個構(gòu)造函數(shù)則是最復(fù)雜的構(gòu)造方法在某方面的簡化版本,可以根據(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)
這種對象池不可以在沒有Jakarta Commmons Collections組件支持的情況下運行。
可以設(shè)定最多能從池中借出多少個對象。
可以設(shè)定池中最多能保存多少個對象。
可以設(shè)定在池中已無對象可借的情況下,調(diào)用它的borrowObject方法時的行為,是等待、創(chuàng)建新的實例還是拋出異常。
可以分別設(shè)定對象借出和還回時,是否進行有效性檢查。
可以設(shè)定是否使用一個單獨的線程,對池內(nèi)對象進行后臺清理。
GenericObjectPool的構(gòu)造方法共有七個,其中:
最簡單的一個是GenericObjectPool(PoolableObjectFactory factory)。僅僅指明要用的PoolableObjectFactory實例,其它參數(shù)則采用默認值。
最復(fù)雜的一個是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ù)maxActive指明能從池中借出的對象的最大數(shù)目。如果這個值不是正數(shù),表示沒有限制。
參數(shù)whenExhaustedAction指定在池中借出對象的數(shù)目已達極限的情況下,調(diào)用它的borrowObject方法時的行為。可以選用的值有:
GenericObjectPool.WHEN_EXHAUSTED_BLOCK,表示等待;
GenericObjectPool.WHEN_EXHAUSTED_GROW,表示創(chuàng)建新的實例(不過這就使maxActive參數(shù)失去了意義);
GenericObjectPool.WHEN_EXHAUSTED_FAIL,表示拋出一個java.util.NoSuchElementException異常。
參數(shù)maxWait指明若在對象池空時調(diào)用borrowObject方法的行為被設(shè)定成等待,最多等待多少毫秒。如果等待時間超過了這個數(shù)值,則會拋出一個java.util.NoSuchElementException異常。如果這個值不是正數(shù),表示無限期等待。
參數(shù)testOnBorrow設(shè)定在借出對象時是否進行有效性檢查。
參數(shù)testOnBorrow設(shè)定在還回對象時是否進行有效性檢查。
參數(shù)timeBetweenEvictionRunsMillis,設(shè)定間隔每過多少毫秒進行一次后臺對象清理的行動。如果這個值不是正數(shù),則實際上不會進行后臺對象清理。
參數(shù)numTestsPerEvictionRun,設(shè)定在進行后臺對象清理時,每次檢查幾個對象。如果這個值不是正數(shù),則每次檢查的對象數(shù)是檢查時池內(nèi)對象的總數(shù)乘以這個值的負倒數(shù)再向上取整的結(jié)果――也就是說,如果這個值是-2(-3、-4、-5……)的話,那么每次大約檢查當(dāng)時池內(nèi)對象總數(shù)的1/2(1/3、1/4、1/5……)左右。
參數(shù)minEvictableIdleTimeMillis,設(shè)定在進行后臺對象清理時,視休眠時間超過了多少毫秒的對象為過期。過期的對象將被回收。如果這個值不是正數(shù),那么對休眠時間沒有特別的約束。
參數(shù)testWhileIdle,則設(shè)定在進行后臺對象清理時,是否還對沒有過期的池內(nèi)對象進行有效性檢查。不能通過有效性檢查的對象也將被回收。
另一個比較特別的構(gòu)造方法是GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) 。其中:
參數(shù)factory指明要與之配合使用的PoolableObjectFactory實例;
參數(shù)config則指明一個包括了各個參數(shù)的預(yù)設(shè)值的對象(詳見《GenericObjectPool.Config》一節(jié))。
剩下的五個構(gòu)造函數(shù)則是最復(fù)雜的構(gòu)造方法在某方面的簡化版本,可以根據(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)
這種對象池不可以在沒有Jakarta Commmons Collections組件支持的情況下運行。
【注意】如果沒有設(shè)置參數(shù)值,GenericObjectPool會使用默認的參數(shù)值,比如池的最大數(shù)為8,這樣可能造成影響。
☆GenericObjectPool.Config
調(diào)用一個有很多的參數(shù)的方法的時候,很可能將參數(shù)的位置和個數(shù)搞錯,導(dǎo)致編譯或運行時的錯誤;閱讀包含了有很多參數(shù)的方法調(diào)用的代碼的時候,也很可能因為沒有搞對參數(shù)的位置和個數(shù),產(chǎn)生錯誤的理解。因此,人們往往避免給一個方法安排太多的參數(shù)的做法(所謂的“Long Parameter List”)。不過,有些方法又確實需要許多參數(shù)才能完成工作。于是,就有人想到了一種將大批的參數(shù)封裝到一個對象(稱為參數(shù)對象,Parameter Object)里,然后將這個對象作為單一的參數(shù)傳遞的兩全其美的對策。
因為生成GenericKeyedObjectPool時可供設(shè)置的特性非常之多,所以它的構(gòu)造方法里也就難免會需要不少的參數(shù)。GenericKeyedObjectPool除了提供了幾個超長的構(gòu)造方法之外,同時也定義了一個使用參數(shù)對象的構(gòu)造方法。所用參數(shù)對象的類型是GenericKeyedObjectPool.Config。
GenericKeyedObjectPool.Config定義了許多的public字段,每個對應(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ù)完全相同。
使用的時候,先生成一個GenericKeyedObjectPool.Config對象,然后將個字段設(shè)置為想要的值,最后用這個對象作為唯一的參數(shù)調(diào)用GenericKeyedObjectPool的構(gòu)造方法即可。
注意:使用有許多public字段、卻沒有任何方法的類,也是一個人們往往加以避免的行為(所謂的“Data Class”)。不過這次GenericKeyedObjectPool特立獨行了一回。
因為生成GenericKeyedObjectPool時可供設(shè)置的特性非常之多,所以它的構(gòu)造方法里也就難免會需要不少的參數(shù)。GenericKeyedObjectPool除了提供了幾個超長的構(gòu)造方法之外,同時也定義了一個使用參數(shù)對象的構(gòu)造方法。所用參數(shù)對象的類型是GenericKeyedObjectPool.Config。
GenericKeyedObjectPool.Config定義了許多的public字段,每個對應(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ù)完全相同。
使用的時候,先生成一個GenericKeyedObjectPool.Config對象,然后將個字段設(shè)置為想要的值,最后用這個對象作為唯一的參數(shù)調(diào)用GenericKeyedObjectPool的構(gòu)造方法即可。
注意:使用有許多public字段、卻沒有任何方法的類,也是一個人們往往加以避免的行為(所謂的“Data Class”)。不過這次GenericKeyedObjectPool特立獨行了一回。
☆帶鍵值的對象池
有時候,單用對池內(nèi)所有對象一視同仁的對象池,并不能解決的問題。例如,對于一組某些參數(shù)設(shè)置不同的同類對象――比如一堆指向不同地址的java.net.URL對象或者一批代表不同語句的java.sql.PreparedStatement對象,用這樣的方法池化,就有可能取出不合用的對象的麻煩。
可以通過為每一組參數(shù)相同的同類對象建立一個單獨的對象池來解決這個問題。但是,如果使用普通的ObjectPool來實施這個計策的話,因為普通的PoolableObjectFactory只能生產(chǎn)出大批設(shè)置完全一致的對象,就需要為每一組參數(shù)相同的對象編寫一個單獨的PoolableObjectFactory,工作量相當(dāng)可觀。這種時候就適合調(diào)遣Pool組件中提供的一種“帶鍵值的對象池”來展開工作了。
Pool組件采用實現(xiàn)了KeyedObjectPool接口的類,來充當(dāng)帶鍵值的對象池。相應(yīng)的,這種對象池需要配合實現(xiàn)了KeyedPoolableObjectFactory接口的類和實現(xiàn)了KeyedObjectPoolFactory接口的類來使用(這三個接口都在org.apache.commons.pool包中定義):
KeyedPoolableObjectFactory和PoolableObjectFactory形式如出一轍,只是每個方法都增加了一個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)來負責(zé)對象出借和歸還的動作。
用void close()來關(guān)閉不再需要的對象池。
用void clear(Object key)和void clear()來清空池中的對象,前者針對與特定鍵值相關(guān)聯(lián)的實例,后者針對整個對象池。
用int getNumActive(Object key)和int getNumActive()來查詢已借出的對象數(shù),前者針對與特定鍵值相關(guān)聯(lián)的實例,后者針對整個對象池。
用int getNumIdle(Object key)和int getNumIdle()來查詢正在休眠的對象數(shù),前者針對與特定鍵值相關(guān)聯(lián)的實例,后者針對整個對象池。
用void setFactory(KeyedPoolableObjectFactory factory)來設(shè)置要用的KeyedPoolableObjectFactory實例。
void clear、int getNumActive、int getNumIdle和void setFactory的各種版本都仍然是可以由具體實現(xiàn)自行決定是否要支持的方法。如果所 用的KeyedObjectPool實現(xiàn)不支持這些操作,那么調(diào)用這些方法的時候,會拋出一個UnsupportedOperationException異常。
KeyedObjectPoolFactory和ObjectPoolFactory的形式完全相同,只是所代表的對象不同而已。
可以通過為每一組參數(shù)相同的同類對象建立一個單獨的對象池來解決這個問題。但是,如果使用普通的ObjectPool來實施這個計策的話,因為普通的PoolableObjectFactory只能生產(chǎn)出大批設(shè)置完全一致的對象,就需要為每一組參數(shù)相同的對象編寫一個單獨的PoolableObjectFactory,工作量相當(dāng)可觀。這種時候就適合調(diào)遣Pool組件中提供的一種“帶鍵值的對象池”來展開工作了。
Pool組件采用實現(xiàn)了KeyedObjectPool接口的類,來充當(dāng)帶鍵值的對象池。相應(yīng)的,這種對象池需要配合實現(xiàn)了KeyedPoolableObjectFactory接口的類和實現(xiàn)了KeyedObjectPoolFactory接口的類來使用(這三個接口都在org.apache.commons.pool包中定義):
KeyedPoolableObjectFactory和PoolableObjectFactory形式如出一轍,只是每個方法都增加了一個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)來負責(zé)對象出借和歸還的動作。
用void close()來關(guān)閉不再需要的對象池。
用void clear(Object key)和void clear()來清空池中的對象,前者針對與特定鍵值相關(guān)聯(lián)的實例,后者針對整個對象池。
用int getNumActive(Object key)和int getNumActive()來查詢已借出的對象數(shù),前者針對與特定鍵值相關(guān)聯(lián)的實例,后者針對整個對象池。
用int getNumIdle(Object key)和int getNumIdle()來查詢正在休眠的對象數(shù),前者針對與特定鍵值相關(guān)聯(lián)的實例,后者針對整個對象池。
用void setFactory(KeyedPoolableObjectFactory factory)來設(shè)置要用的KeyedPoolableObjectFactory實例。
void clear、int getNumActive、int getNumIdle和void setFactory的各種版本都仍然是可以由具體實現(xiàn)自行決定是否要支持的方法。如果所 用的KeyedObjectPool實現(xiàn)不支持這些操作,那么調(diào)用這些方法的時候,會拋出一個UnsupportedOperationException異常。
KeyedObjectPoolFactory和ObjectPoolFactory的形式完全相同,只是所代表的對象不同而已。
☆當(dāng)出借少于歸還
Java并未提供一種機制來保證兩個方法被調(diào)用的次數(shù)之間呈現(xiàn)一種特定的關(guān)系(相等,相差一個常數(shù),或是其它任何關(guān)系)。因此,完全可以做到建立一個ObjectPool對象,然后調(diào)用一次borrowObject方法,借出一個對象,之后重復(fù)兩次returnObject方法調(diào)用,進行兩次歸還。而調(diào)用一個從不曾借出對象的ObjectPool的returnObject方法也并不是一個不可完成的任務(wù)。
盡管這些使用方法并不合乎returnObject的字面意思,但是Pool組件中的各個ObjectPool/KeyedObjectPool實現(xiàn)都不在乎這一點。它們的returnObject方法都只是單純地召喚與當(dāng)前對象池關(guān)聯(lián)的PoolableObjectFactory實例,看這對象能否經(jīng)受得起validateObject的考驗而已??简灥慕Y(jié)果決定了這個對象是應(yīng)該拿去作passivateObject處理,而后留待重用;還是應(yīng)該拿去作destroyObject處理,以免占用資源。也就是說,當(dāng)出借少于歸還的時候,并不會額外發(fā)生什么特別的事情(當(dāng)然,有可能因為該對象池處于不接受歸還對象的請求的狀態(tài)而拋出異常,不過這是常規(guī)現(xiàn)象)。
在實際使用中,可以利用這一特性來向?qū)ο蟪貎?nèi)加入通過其它方法生成的對象。
盡管這些使用方法并不合乎returnObject的字面意思,但是Pool組件中的各個ObjectPool/KeyedObjectPool實現(xiàn)都不在乎這一點。它們的returnObject方法都只是單純地召喚與當(dāng)前對象池關(guān)聯(lián)的PoolableObjectFactory實例,看這對象能否經(jīng)受得起validateObject的考驗而已??简灥慕Y(jié)果決定了這個對象是應(yīng)該拿去作passivateObject處理,而后留待重用;還是應(yīng)該拿去作destroyObject處理,以免占用資源。也就是說,當(dāng)出借少于歸還的時候,并不會額外發(fā)生什么特別的事情(當(dāng)然,有可能因為該對象池處于不接受歸還對象的請求的狀態(tài)而拋出異常,不過這是常規(guī)現(xiàn)象)。
在實際使用中,可以利用這一特性來向?qū)ο蟪貎?nèi)加入通過其它方法生成的對象。
☆什么時候使用對象池
采用對象池化的本意,是要通過減少對象生成的次數(shù),減少花在對象初始化上面的開銷,從而提高整體的性能。然而池化處理本身也要付出代價,因此,并非任何情況下都適合采用對象池化。
Dr. Cliff Click在JavaOne 2003上發(fā)表的《Performance Myths Exposed》中,給出了一組其它條件都相同時,使用與不使用對象池化技術(shù)的實際性能的比較結(jié)果。他的實測結(jié)果表明:
對于類似Point這樣的輕量級對象,進行池化處理后,性能反而下降,因此不宜池化;
對于類似Hashtable這樣的中量級對象,進行池化處理后,性能基本不變,一般不必池化(池化會使代碼變復(fù)雜,增大維護的難度);
對于類似JPanel這樣的重量級對象,進行池化處理后,性能有所上升,可以考慮池化。
根據(jù)使用方法的不同,實際的情況可能與這一測量結(jié)果略有出入。在配置較高的機器和技術(shù)較強的虛擬機上,不宜池化的對象的范圍可能會更大。不過,對于像網(wǎng)絡(luò)和數(shù)據(jù)庫連接這類重量級的對象來說,目前還是有池化的必要。
基本上,只在重復(fù)生成某種對象的操作成為影響性能的關(guān)鍵因素的時候,才適合進行對象池化。如果進行池化所能帶來的性能提高并不重要的話,還是不采用對象池化技術(shù),以保持代碼的簡明,而使用更好的硬件和更棒的虛擬機來提高性能為佳。
Dr. Cliff Click在JavaOne 2003上發(fā)表的《Performance Myths Exposed》中,給出了一組其它條件都相同時,使用與不使用對象池化技術(shù)的實際性能的比較結(jié)果。他的實測結(jié)果表明:
對于類似Point這樣的輕量級對象,進行池化處理后,性能反而下降,因此不宜池化;
對于類似Hashtable這樣的中量級對象,進行池化處理后,性能基本不變,一般不必池化(池化會使代碼變復(fù)雜,增大維護的難度);
對于類似JPanel這樣的重量級對象,進行池化處理后,性能有所上升,可以考慮池化。
根據(jù)使用方法的不同,實際的情況可能與這一測量結(jié)果略有出入。在配置較高的機器和技術(shù)較強的虛擬機上,不宜池化的對象的范圍可能會更大。不過,對于像網(wǎng)絡(luò)和數(shù)據(jù)庫連接這類重量級的對象來說,目前還是有池化的必要。
基本上,只在重復(fù)生成某種對象的操作成為影響性能的關(guān)鍵因素的時候,才適合進行對象池化。如果進行池化所能帶來的性能提高并不重要的話,還是不采用對象池化技術(shù),以保持代碼的簡明,而使用更好的硬件和更棒的虛擬機來提高性能為佳。