一、概述
Apache Commons Pool
庫提供了一整套用於實現物件池化的API,以及若干種各具特色的物件池實現。2.0版本,並非是對1.x的簡單升級,而是一個完全重寫的物件池的實現,顯著的提升了效能和可伸縮性,並且包含可靠的例項跟蹤和池監控。第二版要求JDK1.6+。
二、下載
官方下載頁:
http://commons.apache.org/proper/commons-pool/download_pool.cgi
原始碼:
svn checkout http://svn.apache.org/repos/asf/commons/proper/pool/trunk commons-pool2
Maven工程依賴
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.0</version>
</dependency>
三、使用說明
3.1 建立池化物件
建立池化物件很簡單,只要實現commons-pool
的PooledObjectFactory
工廠介面就行了。
PooledObjectFactory
是一個池化物件工廠介面,定義了生成物件、啟用物件、鈍化物件、銷燬物件的方法,如下:
public interface PooledObjectFactory<T> {
PooledObject<T> makeObject();
void activateObject(PooledObject<T> obj);
void passivateObject(PooledObject<T> obj);
boolean validateObject(PooledObject<T> obj);
void destroyObject(PooledObject<T> obj);
}
它建立並管理PooledObject
。PooledObject
包含了池化的物件例項,以及這些例項的池化屬性,比如建立時間、最後使用時間等等。
如果需要使用Commons-Pool
,那麼你就需要提供一個PooledObjectFactory
介面的具體實現。一個比較簡單的辦法就是,繼承BasePooledObjectFactory
這個抽象類。而繼承這個抽象類,只需要實現兩個方法:create()
和wrap(T obj)
。
實現create()
方法很簡單,而實現wrap(T obj)
也有捷徑,可以使用類DefaultPooledObject
,程式碼可以參考如下:
@Override
public PooledObject<Foo> wrap(Foo foo) {
return new DefaultPooledObject<Foo>(foo);
}
比如,一個完整的例子:
package test.test;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
public class StringBufferFactory extends BasePooledObjectFactory<StringBuffer> {
@Override
public StringBuffer create() throws Exception {
return new StringBuffer();
}
@Override
public PooledObject<StringBuffer> wrap(StringBuffer obj) {
return new DefaultPooledObject<StringBuffer>(obj);
}
}
有時候,單用對池內所有物件一視同仁的物件池,並不能解決問題。例如,有時需要透過key來獲取不同的物件,這樣,就有可能取出不合用的物件的麻煩。當然,可以透過為每一組引數相同的同類物件建立一個單獨的物件池來解決這個問題。但是,如果使用普通的ObjectPool
來實施這個計策的話,因為普通的PooledObjectFactory
只能生產出大批設定完全一致的物件,就需要為每一組引數相同的物件編寫一個單獨的PooledObjectFactory
,工作量相當可觀。這種時候就可以使用BaseKeyedPooledObjectFactory
來替代BasePooledObjectFactory
.這個類,實現的是KeyedPooledObjectFactory
介面,和PooledObjectFactory
介面類似,只是在相關的方法中多了Key
引數,定義如下:
public interface KeyedPoolableObjectFactory<K,V> {
PooledObject<V> makeObject(K key);
void activateObject(K key, PooledObject<V> obj);
void passivateObject(K key, PooledObject<V> obj);
boolean validateObject(K key, PooledObject<V> obj);
void destroyObject(K key, PooledObject<V> obj);
}
3.2 建立物件池
在org.apache.commons.pool2.impl
中預設了三個可以直接使用的物件池:GenericObjectPool
、GenericKeyedObjectPool
和SoftReferenceObjectPool
。
通常使用GenericObjectPool
來建立物件池,如果是物件池是Keyed
的,那麼可以使用GenericKeyedObjectPool
來建立物件池。這兩個類都提供了豐富的配置選項。這兩個物件池的特點是可以設定物件池中的物件特徵,包括LIFO方式、最大空閒數、最小空閒數、是否有效性檢查等等。兩者的區別如前面所述,後者支援Keyed
。
而SoftReferenceObjectPool
物件池,它利用一個java.util.ArrayList
物件來儲存物件池裡的物件。不過它並不在物件池裡直接儲存物件本身,而是儲存它們的“軟引用”(Soft Reference
)。這種物件池的特色是:可以儲存任意多個物件,不會有容量已滿的情況發生;在物件池已空的時候,呼叫它的borrowObject
方法,會自動返回新建立的例項;可以在初始化同時,在池內預先建立一定量的物件;當記憶體不足的時候,池中的物件可以被Java虛擬機器回收。
舉個例子:
new GenericObjectPool<StringBuffer>(new StringBufferFactory());
我們也可以使用GenericObjectPoolConfig
來對上面建立的物件池進行一些引數配置,建立的Config引數,可以使用setConfig
方法傳給物件池,也可以在物件池的構造方法中作為引數傳入。
舉個例子:
GenericObjectPoolConfig conf = new GenericObjectPoolConfig();
conf.setMaxTotal(20);
conf.setMaxIdle(10);
...
GenericObjectPool<StringBuffer> pool = new GenericObjectPool<StringBuffer>(new StringBufferFactory(), conf);
3.3 使用物件池
物件池使用起來很方便,簡單一點就是使用borrowObject
和returnObject
兩個方法,直接給參考程式碼吧:
StringBuffer buf = null;
try {
buf = pool.borrowObject();
...
} catch(IOException e) {
throw e;
} catch(Exception e) {
throw new RuntimeException("Unable to borrow buffer from pool" +
e.toString());
} finally {
try {
if(null != buf) {
pool.returnObject(buf);
}
} catch(Exception e) {
// ignored
}
}