• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 311, comments - 0, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            創(chuàng)建新的對(duì)象并初始化的操作,可能會(huì)消耗很多的時(shí)間。在這種對(duì)象的初始化工作包含了一些費(fèi)時(shí)的操作(例如,從一臺(tái)位于20,000千米以外的主機(jī)上讀出一些數(shù)據(jù))的時(shí)候,尤其是這樣。在需要大量生成這樣的對(duì)象的時(shí)候,就可能會(huì)對(duì)性能造成一些不可忽略的影響。要緩解這個(gè)問題,除了選用更好的硬件和更棒的虛擬機(jī)以外,適當(dāng)?shù)夭捎靡恍┠軌驕p少對(duì)象創(chuàng)建次數(shù)的編碼技巧,也是一種有效的對(duì)策。對(duì)象池化技術(shù)(Object Pooling)就是這方面的著名技巧,而Jakarta Commons Pool組件則是處理對(duì)象池化的得力外援。

            對(duì)象池化技術(shù)

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

            對(duì)于沒有狀態(tài)的對(duì)象(例如String),在重復(fù)使用之前,無需進(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ì)象都適合拿來池化――因?yàn)榫S護(hù)對(duì)象池也要造成一定開銷。對(duì)生成時(shí)開銷不大的對(duì)象進(jìn)行池化,反而可能會(huì)出現(xiàn)“維護(hù)對(duì)象池的開銷”大于“生成新對(duì)象的開銷”,從而使性能降低的情況。但是對(duì)于生成時(shí)開銷可觀的對(duì)象,池化技術(shù)就是提高性能的有效策略了。

            When to use sidebars

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

            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ì)語法和基本概念(對(duì)象、異常、類、接口、實(shí)例、繼承和實(shí)現(xiàn)等)有一般了解即可。

            下載和安裝

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

            以上兩種軟件均有已編譯包和源代碼包兩種形式可供選擇。一般情況下,使用已編譯包即可。不過建議同時(shí)也下載源代碼包,作為參考資料使用。

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

            • Ant
            • JUnit
              • 所需版本:3.8.1+
              • 下載地址: http://www.junit.org
              • 作用:編譯和運(yùn)行單元測試

            具體的編譯方法,可以參看有關(guān)的Ant文檔。

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

            PoolableObjectFactory、ObjectPool和ObjectPoolFactory

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

            • PoolableObjectFactory用于管理被池化的對(duì)象的產(chǎn)生、激活、掛起、校驗(yàn)和銷毀;
            • ObjectPool用于管理要被池化的對(duì)象的借出和歸還,并通知PoolableObjectFactory完成相應(yīng)的工作;
            • ObjectPoolFactory則用于大量生成相同類型和設(shè)置的ObjectPool。

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

            創(chuàng)立PoolableObjectFactory

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

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

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

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

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

            2. 為這個(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;
                              }
                              

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

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

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

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

            5. 為這個(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;
                              }
                              

            6. 為這個(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之后,便可以開始請(qǐng)出ObjectPool來與之同臺(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的使用方法類似這樣:

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

              PoolableObjectFactory factory = new PoolableObjectFactorySample();
                              

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

              ObjectPool pool = new StackObjectPool(factory);
                              

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

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

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

              pool.returnObject(obj);
                              

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

              pool.close();
                              

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

            比較完整的使用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;//明確地設(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)下,無法完成這一操作,會(huì)有一個(gè)IllegalStateException異常拋出。

            利用ObjectPoolFactory

            有時(shí)候,要在多處生成類型和設(shè)置都相同的ObjectPool。如果在每個(gè)地方都重寫一次調(diào)用相應(yīng)構(gòu)造方法的代碼,不但比較麻煩,而且日后修改起來,也有所不便。這種時(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()方法就可以了。日后無論想要調(diào)整所用ObjectPool的參數(shù)還是類型,只需要修改這一處,就可以大功告成了。

             《使用ObjectPool》一節(jié)中的例子,改為使用ObjectPoolFactory來生成所用的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();
                        }
                        }
                        }
                        }
                        

            借助BasePoolableObjectFactory

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

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

            這個(gè)例子展示了一個(gè)從BasePoolableObjectFactory擴(kuò)展而來的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

            可口可樂公司的軟飲料有可口可樂、雪碧和芬達(dá)等品種,百事可樂公司的軟飲料有百事可樂、七喜和美年達(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ì)象來保存對(duì)象池里的對(duì)象。這種對(duì)象池的特色是:

            • 可以為對(duì)象池指定一個(gè)初始的參考大?。ó?dāng)空間不夠時(shí)會(huì)自動(dò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è),其中:

            • 最簡單的一個(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)造方法在某方面的簡化版本,可以根據(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)起來后才能正常使用。

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

            SoftReferenceObjectPool

            SoftReferenceObjectPool利用一個(gè)java.util.ArrayList對(duì)象來保存對(duì)象池里的對(duì)象。不過它并不在對(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è),其中:

            • 最簡單的是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)造方法在某方面的簡化版本,適合在大多數(shù)情況下使用。它是:
              • SoftReferenceObjectPool(PoolableObjectFactory factory)

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

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

            GenericObjectPool

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

            • 可以設(shè)定最多能從池中借出多少個(gè)對(duì)象。
            • 可以設(shè)定池中最多能保存多少個(gè)對(duì)象。
            • 可以設(shè)定在池中已無對(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è),其中:

            • 最簡單的一個(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ù),表示沒有限制。
              • 參數(shù)whenExhaustedAction指定在池中借出對(duì)象的數(shù)目已達(dá)極限的情況下,調(diào)用它的borrowObject方法時(shí)的行為。可以選用的值有:
                • GenericObjectPool.WHEN_EXHAUSTED_BLOCK,表示等待;
                • GenericObjectPool.WHEN_EXHAUSTED_GROW,表示創(chuàng)建新的實(shí)例(不過這就使maxActive參數(shù)失去了意義);
                • GenericObjectPool.WHEN_EXHAUSTED_FAIL,表示拋出一個(gè)java.util.NoSuchElementException異常。
              • 參數(shù)maxWait指明若在對(duì)象池空時(shí)調(diào)用borrowObject方法的行為被設(shè)定成等待,最多等待多少毫秒。如果等待時(shí)間超過了這個(gè)數(shù)值,則會(huì)拋出一個(gè)java.util.NoSuchElementException異常。如果這個(gè)值不是正數(shù),表示無限期等待。
              • 參數(shù)testOnBorrow設(shè)定在借出對(duì)象時(shí)是否進(jìn)行有效性檢查。
              • 參數(shù)testOnBorrow設(shè)定在還回對(duì)象時(shí)是否進(jìn)行有效性檢查。
              • 參數(shù)timeBetweenEvictionRunsMillis,設(shè)定間隔每過多少毫秒進(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é)果――也就是說,如果這個(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í)間超過了多少毫秒的對(duì)象為過期。過期的對(duì)象將被回收。如果這個(gè)值不是正數(shù),那么對(duì)休眠時(shí)間沒有特別的約束。
              • 參數(shù)testWhileIdle,則設(shè)定在進(jìn)行后臺(tái)對(duì)象清理時(shí),是否還對(duì)沒有過期的池內(nèi)對(duì)象進(jìn)行有效性檢查。不能通過有效性檢查的對(duì)象也將被回收。
              • 另一個(gè)比較特別的構(gòu)造方法是GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) 。其中:
                • 參數(shù)factory指明要與之配合使用的PoolableObjectFactory實(shí)例;
                • 參數(shù)config則指明一個(gè)包括了各個(gè)參數(shù)的預(yù)設(shè)值的對(duì)象(詳見《GenericObjectPool.Config》一節(jié))。
              • 剩下的五個(gè)構(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)

            這種對(duì)象池不可以在沒有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)闆]有搞對(duì)參數(shù)的位置和個(gè)數(shù),產(chǎn)生錯(cuò)誤的理解。因此,人們往往避免給一個(gè)方法安排太多的參數(shù)的做法(所謂的“Long Parameter List”)。不過,有些方法又確實(shí)需要許多參數(shù)才能完成工作。于是,就有人想到了一種將大批的參數(shù)封裝到一個(gè)對(duì)象(稱為參數(shù)對(duì)象,Parameter Object)里,然后將這個(gè)對(duì)象作為單一的參數(shù)傳遞的兩全其美的對(duì)策。

            因?yàn)樯蒅enericKeyedObjectPool時(shí)可供設(shè)置的特性非常之多,所以它的構(gòu)造方法里也就難免會(huì)需要不少的參數(shù)。GenericKeyedObjectPool除了提供了幾個(gè)超長的構(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字段、卻沒有任何方法的類,也是一個(gè)人們往往加以避免的行為(所謂的“Data Class”)。不過這次GenericKeyedObjectPool特立獨(dú)行了一回。

            帶鍵值的對(duì)象池

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

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

            Pool組件采用實(shí)現(xiàn)了KeyedObjectPool接口的類,來充當(dāng)帶鍵值的對(duì)象池。相應(yīng)的,這種對(duì)象池需要配合實(shí)現(xiàn)了KeyedPoolableObjectFactory接口的類和實(shí)現(xiàn)了KeyedObjectPoolFactory接口的類來使用(這三個(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)來負(fù)責(zé)對(duì)象出借和歸還的動(dòng)作。
              • 用void close()來關(guān)閉不再需要的對(duì)象池。
              • 用void clear(Object key)和void clear()來清空池中的對(duì)象,前者針對(duì)與特定鍵值相關(guān)聯(lián)的實(shí)例,后者針對(duì)整個(gè)對(duì)象池。
              • 用int getNumActive(Object key)和int getNumActive()來查詢已借出的對(duì)象數(shù),前者針對(duì)與特定鍵值相關(guān)聯(lián)的實(shí)例,后者針對(duì)整個(gè)對(duì)象池。
              • 用int getNumIdle(Object key)和int getNumIdle()來查詢正在休眠的對(duì)象數(shù),前者針對(duì)與特定鍵值相關(guān)聯(lián)的實(shí)例,后者針對(duì)整個(gè)對(duì)象池。
              • 用void setFactory(KeyedPoolableObjectFactory factory)來設(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基本一致,只是原來使用GenericObjectPool.Config的地方要使用GenericKeyedObjectPool.Config代替。

            當(dāng)出借少于歸還

            Java并未提供一種機(jī)制來保證兩個(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處理,以免占用資源。也就是說,當(dāng)出借少于歸還的時(shí)候,并不會(huì)額外發(fā)生什么特別的事情(當(dāng)然,有可能因?yàn)樵搶?duì)象池處于不接受歸還對(duì)象的請(qǐng)求的狀態(tài)而拋出異常,不過這是常規(guī)現(xiàn)象)。

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

            線程安全問題

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

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

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

            例如,這段代碼有些時(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();
                        }
                        }
                        }
                        

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

            什么時(shí)候不要池化

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

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

            • 對(duì)于類似Point這樣的輕量級(jí)對(duì)象,進(jìn)行池化處理后,性能反而下降,因此不宜池化;
            • 對(duì)于類似Hashtable這樣的中量級(jí)對(duì)象,進(jìn)行池化處理后,性能基本不變,一般不必池化(池化會(huì)使代碼變復(fù)雜,增大維護(hù)的難度);
            • 對(duì)于類似JPanel這樣的重量級(jí)對(duì)象,進(jìn)行池化處理后,性能有所上升,可以考慮池化。

            根據(jù)使用方法的不同,實(shí)際的情況可能與這一測量結(jié)果略有出入。在配置較高的機(jī)器和技術(shù)較強(qiáng)的虛擬機(jī)上,不宜池化的對(duì)象的范圍可能會(huì)更大。不過,對(duì)于像網(wǎng)絡(luò)和數(shù)據(jù)庫連接這類重量級(jí)的對(duì)象來說,目前還是有池化的必要。

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

            結(jié)束語

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

            久久久久久亚洲精品成人| 热RE99久久精品国产66热| 久久国产精品77777| 久久精品国产亚洲av日韩| 久久精品亚洲一区二区三区浴池 | 99久久做夜夜爱天天做精品| 亚洲性久久久影院| 久久婷婷国产综合精品| 久久r热这里有精品视频| 精品久久久久久无码免费| 久久婷婷五月综合国产尤物app| 久久久久高潮毛片免费全部播放| 国产一区二区精品久久凹凸| 2021国内精品久久久久久影院| 久久国产热精品波多野结衣AV| 国内精品伊人久久久久影院对白| 亚洲欧美国产精品专区久久| A狠狠久久蜜臀婷色中文网| 精品无码久久久久久国产| 狠狠色狠狠色综合久久| 久久国产一区二区| 国产精品中文久久久久久久| 国产精品久久久久久| 久久毛片一区二区| 久久精品国产91久久麻豆自制| 狠狠色丁香久久婷婷综合蜜芽五月| 97久久超碰国产精品2021| 亚洲人成网站999久久久综合| 国产精品青草久久久久婷婷| 国产精品一区二区久久精品涩爱| 99久久精品国产高清一区二区 | 精品久久久久久成人AV| 无码任你躁久久久久久| 久久亚洲高清观看| 久久亚洲美女精品国产精品| 伊人精品久久久久7777| 国产精品欧美亚洲韩国日本久久| 国产麻豆精品久久一二三| 中文字幕无码免费久久| 日本欧美国产精品第一页久久| 久久综合久久综合久久|