Apache Common-pool2對象池分析和應用


Apache Common-pool2包提供了一個通用的對象池技術的實現。可以很方便的基於它來實現自己的對象池,比如DBCP和Jedis他們的內部對象池的實現就是依賴於Common-pool2。
對象的創建和銷毀在一定程度上會消耗系統的資源,雖然jvm的性能在近幾年已經得到了很大的提高,對於多數對象來說,沒有必要利用對象池技術來進行對象的創建和管理。但是對於有些對象來說,其創建的代價還是比較昂貴的,比如線程、tcp連接、數據庫連接等對象,因此對象池技術還是有其存在的意義。
實現分析
Common-pool2由三大模塊組成:ObjectPool、PooledObject和PooledObjectFactory。
ObjectPool:提供所有對象的存取管理。
PooledObject:池化的對象,是對對象的一個包裝,加上了對象的一些其他信息,包括對象的狀態(已用、空閑),對象的創建時間等。
PooledObjectFactory:工廠類,負責池化對象的創建,對象的初始化,對象狀態的銷毀和對象狀態的驗證。
ObjectPool會持有PooledObjectFactory,將具體的對象的創建、初始化、銷毀等任務交給它處理,其操作對象是PooledObject,即具體的Object的包裝類。
org.apache.commons.pool2.impl 包提供了一個默認的對象池實現。
主要還是這三個模塊的實現,其中PooledObjectFactory在包里沒有具體實現,因為這涉及到具體對象的創建,需要應用本身去實現,這也體現了設計上的解耦合性。
BaseGenericObjectPool
它主要定義了對象池的一些配置信息和實現jmx注冊注銷等功能。
以下是對象池的相關配置
GenericObjectPool
數據結構:ConcurrentHashMap和LinkedBlockingDeque。前者用於存儲所有的對象(不含銷毀的對象),后者用於存儲空閑的對象。
borrowObject()大體思路如下
1 從LinkedBlockingDeque中pollFirst
2 若為空,檢查對象池對象是否達到上限,若是重復1,若否,則調用PooledObjectFactory的makeObject去創建一個對象
3 得到對象之后,對對象進行初始化和一些配置的計數處理,同時將對象加入到ConcurrentHashMap。
returnObject(T obj)大體思路如下
1 根據obj從ConcurrentHashMap拿到其對應的PooledObject p
2 判空;將p狀態置為RETURN
3 若getTestOnReturn參數為true,進行validateObject
4 對p進行passivateObject,與初始化相反
5 更新p狀態為IDLE
6 歸還Pool:Pool的idle實例達到上限或者Pool已經關閉,銷毀之,否則將p加入到LinkedBlockingDeque中。
DefaultPooledObject
默認的PooledObject實現,維護池化對象的一系列狀態參數。

 

簡介

在以往的一些工作中總接觸到一個池的概念。這些池包括有線程池,對象池和連接池等。從池本身的概念來說,它是將一系列的資源事先准備好放在一個地方,等需要的時候直接拿過去用。而用完之后再放回來。和我們平常的需要使用資源再創建的方式相比,這種池的方式節省了創建和銷毀資源的這么一個過程。所以說,對於一些比較比較稀缺的資源或者創建和銷毀影響系統性能的資源,采用池的方式可以有效的提高整體性能。最近一段時間正好要用到一些數據庫連接池等東西,趁這個時機把一些相關的部分好好的學習總結一下。如前面提到的,我們使用到的典型的線程池有jdk里的ExecutorService相關的一系列工具類(在這篇文章里有闡述),而一些典型的數據庫連接池有cp03和dbcp。最近用到了dbcp,而它主要是基於commons-pool實現的,所以我們先從commons-pool2開始吧。

整體結構分析

在正式分析commons-pool2的結構開始,我們設想一下,如果我們自己來設計一個object pool的話,我們該怎么來做呢? 首先一個,我們從直接使用的角度來說,肯定需要一個ObjectPool對象,它負責保存我們需要訪問的對象,我們把這個對象用完之后就返還給它。更細化一點的考慮的話,我們在使用前需要根據配置事先創建一些對象,后面需要使用的時候直接取就可以了。然后我們需要能夠知道pool里面有多少可以用的對象。還有一個就是對象在被使用前是一個類似於初始化的狀態,而使用完之后可能是另外的一個狀態,既然我們希望對象能夠被復用,在返回到pool里的時候可能還需要做一些重置的工作。 除了這些以外,如果我們考慮對象的創建。我們可以將創建對象的職責給pool,這樣pool同時既要管理對象的存取和回收又要管對象的創建和狀態設置等。這有點像一個圖書館的管理員,他既要管借還書,還要管怎么造紙印書。這樣想來,似乎管的職責有點多了。我們也可以將對象的創建單獨分離處理作為一個factory來做。 其實,通過前面的這些討論,我們幾乎也能創建一個像模像樣的pool。如果結合commons-pool2的詳細設計,我們會發現它的設計思路和我們也是基本上一致的。它主要由3個大的部分組成: ObjectPool:專門實現對象存取和狀態管理的池實現。我們直接操作的線程池就是定義在這里。值得注意的一點是這里定義的只是怎么來獲取以及釋放對象等操作,至於具體對象是怎么創建的,一般都通過獨立的一個PooledObjectFactory來操心了。 PooledObject:這是commons-pools里比較有意思的一個類族。是對需要放到池里對象的一個包裝類。添加了一些附加的信息,比如說狀態信息,創建時間,激活時間,關閉時間等。這些添加的信息方便pool來管理和實現一些特定的操作。 PooledObjectFactory: 如我們前面所討論的,管理具體對象的狀態,比如創建,初始化,驗證對象狀態和銷毀對象。 通過我們的這些討論,他們這三者就構成了一個object pool的基本框架。他們的關系可以用如下的一個圖來描述: 用一句話來概括他們整體的關系就是factory創建需要放入pool的對象,經過PooledObject包裝一下就可以上架了。 有一個地方需要注意一下,雖然我們這里將它划分成3個主要的部分,從更精確的細節來看,每一個類族里都有一些不同的實現,這里我們再針對每一類族做一個大體的介紹。 ObjectPool類族 ObjectPool類族包含了以ObjectPool為代表的一系列pool,其中最主要的兩類就是ObjectPool和KeyedObjectPool,他們分別針對普通的pool和以名值對映射的pool。和他們相關的整體幾個類結構如下圖:

從前面的圖里頭我們可以看到,真正定義的接口就是ObjectPool和KeyedObjectPool,具體的實現里有GenericObjectPool, GenericKeyedObjectPool和SoftReferenceObjectPool。不管是哪一種他們都引用了PooledObject。 這里我們不詳細分析具體實現的代碼,我們先就前面的設想來驗證一下他們的整體思路。既然是ObjectPool,那它就應該管borrowObject, returnObject之類的了。是不是呢?我們先看看ObjectPool這一塊的代碼:

這部分的代碼很簡單直接,主要就是包含了一個pool需要的基本操作功能,比如從pool里取對象的borrowObject,返回對象到pool里的returnObject,還有一些查找里面可用對象數量以及管理pool的方法。這里的clear方法是用來清空里面處於idle狀態的對象,而close方法則用來關閉整個pool。他們兩者的一個典型差別就是clear之后里面沒有可以用的對象了,我們需要再創建一些對象放進去。而close之后則連pool都訪問不了了。 與ObjectPool對象的一個是KeyedObjectPool,它的操作方法基本上和ObjectPool一樣,唯一的差別是它是基於key來操作的,所以所有相關的borrowObject, returnObject等操作都要提供key參數。 PooledObject類族 如前所述,PooledObject主要是對需要被加入到pool里的對象提供一個包裝,方便來查看或者統計一些對象信息,比如某個對象創建的時間,空閑時間以及活躍時間等。下面是PooledObject相關

的類關系圖: 

既然是一個簡單的包裝,我們就來看看具體需要訪問的一些方法。如下是PooledObject里的基本方法定義:

這里列出的一大堆方法其實就是標注了各種操作,很多都是PooledObject本身實現加上去的。ok,有了前面這些管理對象和包裝對象的東西,我們再來看看具體制造和銷毀對象的部分。 PooledObjectFactory 在commons-pool2里,我們專門抽象出來一個創建和銷毀對象的接口,PooledObjectFactory。而具體對象的創建則由用戶自定義實現。在本身的源代碼里只有一個簡單的抽象類BasePooledObjectFactory實現。我們來看看這個接口定義的方法:

public interface PooledObjectFactory<t> {
  PooledObject<t> makeObject() throws Exception;
  void destroyObject(PooledObject<t> p) throws Exception;
  boolean validateObject(PooledObject<t> p);
  void activateObject(PooledObject<t> p) throws Exception;
  void passivateObject(PooledObject<t> p) throws Exception;
}

 

makeObject和destroyObject則分別用於創建銷毀對象,很簡單。而validateObject用於在每次創建對象放入到pool里或者從pool里取對象出來的時候驗證是否對象合法。至於activeObject和passivateObject比較有意思。在這里給對象定義了一系列的狀態。這里的設計假定所有在pool里可以被立馬拿出去用的對象是一個idle狀態,在拿到之后要用activateObject方法來激活一下。而對象要返回給pool的時候則要用passivateObject方法將它鈍化。 和ObjectPool相對應的,PooledObjectFactory也有一個相應的KeyedPooledObjectFactory,他們的區別也一樣,一個是基於key來操作一個不是。 其他 除了我們前面列舉出來的這3個部分,還有org.apache.commons.pool2.impl和org.apache.commons.pool2.proxy這兩個包。他們分別定義了前面定義的pool的默認實現和基於代理的實現。在后續的文章里會針對每個部分的細節實現做一個分析。 總結 commons-pool2的整體結構並不復雜,可以將其划分為3個角色,一個管對象的進出口,一個管對象的包裝,一個對象的創建。感覺好像一個產銷結合的商業團體啊。有意思。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM