1. 程式人生 > >純手寫資料庫連線池

純手寫資料庫連線池

資料庫連線池原理

基本原理

在內部物件池中,維護一定數量的資料庫連線,並對外暴露資料庫連線的獲取和返回方法。

如外部使用者可通過getConnection方法獲取資料庫連線,使用完畢後再通過releaseConnection方法將連線返回,注意此時的連線並沒有關閉,而是由連線池管理器回收,併為下一次使用做好準備。

執行緒池作用

 ①資源重用

      由於資料庫連線得到重用,避免了頻繁建立、釋放連線引起的大量效能開銷。在減少系統消耗的基礎上,增進了系統環境的平穩性(減少記憶體碎片以級資料庫臨時程序、執行緒的數量)

   ②更快的系統響應速度

      資料庫連線池在初始化過程中,往往已經建立了若干資料庫連線置於池內備用。此時連線池的初始化操作均已完成。對於業務請求處理而言,直接利用現有可用連線,避免了資料庫連線初始化和釋放過程的時間開銷,從而縮減了系統整體響應時間。

   ③新的資源分配手段

      對於多應用共享同一資料庫的系統而言,可在應用層通過資料庫連線的配置,實現資料庫連線技術。

   ④統一的連線管理,避免資料庫連線洩露

     在較為完備的資料庫連線池實現中,可根據預先的連線佔用超時設定,強制收回被佔用的連線,從而避免了常規資料庫連線操作中可能出現的資源洩露

 常用資料庫連線池

C3P0

C3P0是一個開放原始碼的JDBC連線池,它在lib目錄中與Hibernate一起釋出,包括了實現jdbc3和jdbc2擴充套件規範說明的Connection 和Statement 池的DataSources 物件。

BoneCP

BoneCP 是一個開源的快速的 JDBC 連線池。BoneCP很小,只有四十幾K(執行時需要log4j和Google Collections的支援,這二者加起來就不小了),而相比之下 C3P0 要六百多K。另外個人覺得 BoneCP 有個缺點是,JDBC驅動的載入是在連線池之外的,這樣在一些應用伺服器的配置上就不夠靈活。當然,體積小並不是 BoneCP 優秀的原因,BoneCP 到底有什麼突出的地方呢,請看看效能測試報告。池,Tomcat的資料來源使用的就是DBCP。目前 DBCP 有兩個版本分別是 1.3 和 1.4。1.3 版

DBCP

DBCP (Database Connection Pool)是一個依賴Jakarta commons-pool物件池機制的資料庫連線本對應的是 JDK 1.4-1.5 和 JDBC 3,而1.4 版本對應 JDK 1.6 和 JDBC 4。因此在選擇版本的時候要看看你用的是什麼 JDK 版本了,功能上倒是沒有什麼區別。

Proxool

Proxool是一個Java SQL Driver驅動程式,提供了對你選擇的其它型別的驅動程式的連線池封裝。可以非常簡單的移植到現存的程式碼中。完全可配置。快速,成熟,健壯。可以透明地為你現存的JDBC驅動程式增加連線池功能。

DBCP與C3P0區別

dbcp沒有自動的去回收空閒連線的功能  c3p0有自動回收空閒連線功能  兩者主要是對資料連線的處理方式不同!C3P0提供最大空閒時間,DBCP提供最大連線數。 前者當連線超過最大空閒連線時間時,當前連線就會被斷掉。DBCP當連線數超過最大連線數時,所有連線都會被斷

讀取外部配置資訊

//外部配置檔案資訊
public class DbBean {

	/* 連結屬性 */
	private String driverName = "com.mysql.jdbc.Driver";

	private String url = "jdbc:mysql://localhost:3306/test";

	private String userName = "root";

	private String password = "mysql";

	private String poolName = "thread01";// 連線池名字

	private int minConnections = 1; // 空閒池,最小連線數

	private int maxConnections = 10; // 空閒池,最大連線數

	private int initConnections = 5;// 初始化連線數

	private long connTimeOut = 1000;// 重複獲得連線的頻率

	private int maxActiveConnections = 100;// 最大允許的連線數,和資料庫對應

	private long connectionTimeOut = 1000 * 60 * 20;// 連線超時時間,預設20分鐘

	public String getDriverName() {
		return driverName;
	}

	public void setDriverName(String driverName) {
		this.driverName = driverName;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getPoolName() {
		return poolName;
	}

	public void setPoolName(String poolName) {
		this.poolName = poolName;
	}

	public int getMinConnections() {
		return minConnections;
	}

	public void setMinConnections(int minConnections) {
		this.minConnections = minConnections;
	}

	public int getMaxConnections() {
		return maxConnections;
	}

	public void setMaxConnections(int maxConnections) {
		this.maxConnections = maxConnections;
	}

	public int getInitConnections() {
		return initConnections;
	}

	public void setInitConnections(int initConnections) {
		this.initConnections = initConnections;
	}

	public long getConnTimeOut() {
		return connTimeOut;
	}

	public void setConnTimeOut(long connTimeOut) {
		this.connTimeOut = connTimeOut;
	}

	public int getMaxActiveConnections() {
		return maxActiveConnections;
	}

	public void setMaxActiveConnections(int maxActiveConnections) {
		this.maxActiveConnections = maxActiveConnections;
	}

	public long getConnectionTimeOut() {
		return connectionTimeOut;
	}

	public void setConnectionTimeOut(long connectionTimeOut) {
		this.connectionTimeOut = connectionTimeOut;
	}

}

建立資料庫連線池

/**
 * 資料庫連線池<br>
 * 
 * 1.初始化<br>
 * ####執行緒池核心容器 空閒執行緒數、活動執行緒數<br>
 * ###建構函式 1.1.1初始化執行緒,存放在空閒執行緒池中<br>
 * 2.獲取連線 <br>
 * ####1.判斷存線上程數是否大於最大執行緒 如果大於最大執行緒數,則進行等待...<br>
 * ####2.判斷空閒執行緒數是否大於0 如果空閒執行緒數<0,建立新的連線<br>
 * ####3.如果空閒執行緒數>0,則獲取當前空閒執行緒,存入在活動執行緒集合中 <br>
 * 3.釋放連線 <br>
 * ####3.1.1.判斷空閒執行緒數是否大於最大執行緒數 <br>
 * ####3.1.2.如果空閒執行緒數小於最大執行緒數,將該連線收回到 空閒 執行緒集合中<br>
 * ####3.1.3.刪除該連線對應的活動執行緒集合資料<br>
 */
public class ConnectionPool implements IConnectionPool {

	// 空閒執行緒集合
	private List<Connection> freeConnection = new Vector<Connection>();
	// 活動執行緒集合
	private List<Connection> activeConnection = new Vector<Connection>();
	// 記錄執行緒總數
	private static int connCount = 0;
	private DbBean dbBean;

	public ConnectionPool(DbBean dbBean) {
		this.dbBean = dbBean;
		init();
	}

	public void init() {
		try {

			for (int i = 0; i < dbBean.getInitConnections(); i++) {
				Connection newConnection = newConnection();
				if (newConnection != null) {
					// 新增到空閒執行緒中...
					freeConnection.add(newConnection);
				}
			}

		} catch (Exception e) {

		}
	}

	// 建立新的Connection
	private Connection newConnection() {
		try {
			if (dbBean == null) {
				return null;
			}
			Class.forName(dbBean.getDriverName());
			Connection connection = DriverManager.getConnection(dbBean.getUrl(), dbBean.getUserName(),
					dbBean.getPassword());
			connCount++;
			return connection;
		} catch (Exception e) {
			return null;
		}

	}

	public Connection getConnection() {
		// * ####1.判斷活動執行緒數是否大於最大執行緒 如果大於最大執行緒數,則進行等待...<br>
		Connection connection = null;
		try {

			if (connCount < dbBean.getMaxActiveConnections()) {
				// 還有活動執行緒可以使用
				// * ####2.判斷空閒執行緒數是否大於0 如果空閒執行緒數<0,建立新的連線<br>
				if (freeConnection.size() > 0) {
					connection = freeConnection.remove(0);// 等於freeConnection.get(0);freeConnection.remove(0);
				} else {
					// 建立新的連線
					connection = newConnection();
				}

				boolean available = isAvailable(connection);
				if (available) {
					activeConnection.add(connection);
				} else {
					connCount--;// i--操作
					connection = getConnection();// 遞迴呼叫getConnection方法
				}
			} else {
				// 大於最大執行緒數,進行等待,重新獲取連線
				wait(dbBean.getConnTimeOut());
				connection = getConnection();// 遞迴呼叫getConnection方法
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		// * ####3.如果空閒執行緒數>0,則獲取當前空閒執行緒,存入在活動執行緒集合中 <br>
		return connection;
	}

	// 判斷連線是否可用
	public boolean isAvailable(Connection connection) {
		try {
			if (connection == null || connection.isClosed()) {
				return false;
			}

		} catch (Exception e) {
			// TODO: handle exception
		}
		return true;

	}

	public void releaseConnection(Connection connection) {
		try {
			if (connection == null) {
				return;
			}
			if (isAvailable(connection)) {
				// 判斷空閒執行緒數是否大於最大執行緒數
				if (freeConnection.size() < dbBean.getMaxConnections()) {
					freeConnection.add(connection);
				} else {
					// 空閒執行緒數已經滿了
					connection.close();
				}
				activeConnection.remove(connection);
				connCount--;
				notifyAll();

			}

		} catch (Exception e) {

		}

	}

}

測試執行結果

public class Test001 {

	public static void main(String[] args) {

		DBThread dBThread = new DBThread();
		for (int i = 1; i <= 3; i++) {
			Thread thread = new Thread(dBThread, "使用者執行緒" + i);
			thread.start();
		}

	}

}

class DBThread implements Runnable {

	public void run() {
		for (int i = 0; i < 10; i++) {
			Connection connection = ConnectionPoolManager.getConnection();
			System.out.println(Thread.currentThread().getName() + ",connection:" + connection);
			ConnectionPoolManager.releaseConnection(connection);
		}
	}

}