物件池模式是創建型模式的一種,透過預先建立並重複使用物件,避免頻繁建立/銷毀帶來的效能開銷。本文提供一個通用的抽象父類別 ObjectPool<T>,並以 JDBCConnectionPool 為具體實作範例,展示如何將資料庫連線納入池化管理。文末另附一個基於 Semaphore 的陣列式池化示意,供進階參考。
import java.util.Iterator;
import java.util.Vector;
public abstract class ObjectPool<T> {
private Vector<T> locked, unlocked; // locked=已借出,unlocked=可用
public ObjectPool() {
locked = new Vector<T>();
unlocked = new Vector<T>();
}
// 子類別必須實作:建立新物件
protected abstract T create();
// 子類別必須實作:驗證物件是否仍可用
public abstract boolean validate(T o);
// 子類別必須實作:銷毀失效物件
public abstract void expire(T o);
// 從池中取得物件
public synchronized T checkOut() {
T t;
if (unlocked.size() > 0) {
Iterator<T> iter = unlocked.iterator();
while (iter.hasNext()) {
t = iter.next();
if (validate(t)) { // 物件有效
unlocked.remove(t);
locked.add(t);
return t;
} else { // 物件已失效
unlocked.remove(t);
expire(t);
}
}
}
// 池中無可用物件,建立新物件
t = create();
locked.add(t);
return t;
}
// 歸還物件到池中
public synchronized void checkIn(T t) {
locked.remove(t);
if (validate(t)) {
unlocked.add(t);
} else {
expire(t);
}
}
}
locked 與 unlocked 分離,避免遍歷整個池子判斷狀態。create()、validate()、expire() 交由子類別實作,父類別只管流程。synchronized,簡單有效;高併發場景可改用 ReentrantLock 或無鎖資料結構。import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JDBCConnectionPool extends ObjectPool<Connection> {
private String url, usr, pwd;
public JDBCConnectionPool(String driver, String url, String usr, String pwd) {
super();
try {
Class.forName(driver).newInstance(); // 載入驅動
} catch (Exception e) {
e.printStackTrace();
}
this.url = url;
this.usr = usr;
this.pwd = pwd;
}
@Override
protected Connection create() {
try {
return DriverManager.getConnection(url, usr, pwd);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean validate(Connection conn) {
try {
return !conn.isClosed(); // 修正:原碼回傳 isClosed() 邏輯反了
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
@Override
public void expire(Connection conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
conn = null;
}
}
public static void main(String[] args) {
JDBCConnectionPool pool = new JDBCConnectionPool(
"com.mysql.jdbc.Driver",
"jdbc:mysql://127.0.0.1:3306/test",
"root", "123");
Connection conn = pool.checkOut();
// 使用連線...
pool.checkIn(conn);
}
}
原始碼中 validate() 回傳 conn.isClosed(),邏輯應為「未關閉才有效」,已於上方修正為 !conn.isClosed()。
以下示意碼展示如何用 Semaphore 控制最大可用數量,搭配陣列與布林旗標管理物件狀態,適合物件大小固定、效能極度敏感的場景。
import java.util.concurrent.Semaphore;
class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
protected Object[] items = new Object[MAX_AVAILABLE]; // 實際物件陣列
protected boolean[] used = new boolean[MAX_AVAILABLE];
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null; // 理論上不會到達
}
protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
}
connections = ((core_count * 2) + effective_spindle_count),並依實際負載壓測調整。connectionTestQuery(如 SELECT 1)或 JDBC4 的 isValid(timeout),避免借出斷線連線。leakDetectionThreshold 記錄長時間未歸還的連線堆疊。