首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 設計模式:物件池模式完整實作與 JDBC 連線池範例

     編輯:Java 相關知識

前言

物件池模式是創建型模式的一種,透過預先建立並重複使用物件,避免頻繁建立/銷毀帶來的效能開銷。本文提供一個通用的抽象父類別 ObjectPool<T>,並以 JDBCConnectionPool 為具體實作範例,展示如何將資料庫連線納入池化管理。文末另附一個基於 Semaphore 的陣列式池化示意,供進階參考。

核心抽象類別:ObjectPool<T>

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 或無鎖資料結構。

具體實作:JDBCConnectionPool

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 的陣列式物件池

以下示意碼展示如何用 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;
    }
}

實務建議

  1. 生產環境優先使用成熟框架:HikariCP、Druid、Apache DBCP2 已處理連線驗證、閒置回收、指標監控等細節。
  2. 池大小設定:參考公式 connections = ((core_count * 2) + effective_spindle_count),並依實際負載壓測調整。
  3. 驗證機制:啟用 connectionTestQuery(如 SELECT 1)或 JDBC4 的 isValid(timeout),避免借出斷線連線。
  4. 洩漏偵測:開啟 leakDetectionThreshold 記錄長時間未歸還的連線堆疊。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved