對象池模式
對象池模式,也稱對象池服務,通過循環使用對象,減少資源在初始化和釋放時的昂貴損耗。
簡單地說,在需要時,從池中提取;不用時,放回池中,等待下一個請求。典型例子是連接池和線程池。
public abstract class ObjectPool<T> {
//容器,容納對象
private Hashtable<T, ObjectStatus> pool = new Hashtable<T, ObjectStatus>();
//初始化時創建對象,並放入到池中
public ObjectPool() {
pool.put(create(), new ObjectStatus());
}
//從Hashtable中取出空閒元素
public synchronized T checkOut() {
//這是最簡單的策略
for (T t : pool.keySet()) {
if (pool.get(t).validate()) {
pool.get(t).setUsing();
return t;
}
}
return null;
}
//歸還對象
public synchronized void checkIn(T t) {
pool.get(t).setFree();
}
class ObjectStatus {
//佔用
public void setUsing() {
}
//釋放
public void setFree() {
//注意:若T是有狀態,則需要回歸到初始化狀態
}
//檢查是否可用
public boolean validate() {
return false;
}
}
//創建池化對象
public abstract T create();
}
對象池模式的實際應用
有一個專業的新聞追蹤網站,最近一段時間內出現偶發性緩慢,從監控情況上看,響應時間在2秒以上。網站首頁內容基本都是靜態的(輪詢生成),唯一的動態部分是網站的激勵語,如“積一時之跬步,臻千里之遙程”、“業精於勤,荒於嬉;行成於思,毀於隨”等勵志語句,這是一個簡單的SQL隨機查詢結果,表中的數量在5000條左右,而且結構簡單,查詢性能不是問題。
//原語句
@Service
public class WisdomProvider {
@Autowire
private WisdomDao wisdomDao;
public String getOneWord() {
return wisdomDao.randomOneWisdom();
}
}
在跟蹤過程中,發現高峯期數據庫連接偶爾出現佔滿情況,而且都是查詢該表(該數據庫的隨機查詢算法有缺陷),問題找到了:每一次訪問都會直接查詢數據庫,沒有緩存。通常情況下,這沒有問題,但是在高併發的情況下,例如在10萬PV的壓力下服務器基本就垮掉了,這是非常嚴重的問題。因此,引入一個對象池,把這5000條記錄(根據評估最多不超過20000條記錄)在啓動時直接加載到內存中,在需要時再從內存中取得,以後查詢不再與數據庫交互。
@Service
public class WisdomProvider {
@Autowire
private WisdomDao wisdomDao;
private List<String> wisdoms = null;
@PostConstruct
public void init() {
wisdoms = wisdomDao.getAll();
}
public String getOneWord() {
return RandomUtils.getOne(wisdoms);
}
}
@PostConstruct註解的作用是Spring容器在啓動完畢後,直接執行init方法,一次性讀取所有的數據,然後在應用運行期間不再與數據庫交互,直接從List列表中獲取數據。完事兒後系統性能大幅提升,在不增加硬件的情況下,徹底解決了性能問題。