1. 程式人生 > >Java使用動態代理編寫資料庫連線池

Java使用動態代理編寫資料庫連線池

在上一篇部落格中,將5個連線放到棧裡,當做資料庫連線池使用,加快了效率。程式碼如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ResourceBundle;
import java.util.Stack;

public class DataPool {
	private static Stack<Connection> pool = new Stack<Connection>();
	static {
		try {
		//讀取配置檔案的資訊
			ResourceBundle bundle =
ResourceBundle.getBundle("jdbc"); String Driver = bundle.getString("jdbc.driver"); String url = bundle.getString("jdbc.url"); String user = bundle.getString("jdbc.username"); String password = bundle.getString("jdbc.password"); Integer size = Integer.valueOf(bundle.getString("jdbc.initialSize"
)); Class.forName(Driver);//// 另一種寫法new com.mysql.jdbc.Driver();相當於driver寫入記憶體 for (int i = 0; i < size; i++) { Connection connection = DriverManager.getConnection(url, user, password);//建立連線 pool.push(connection); } } catch (Exception e) { e.printStackTrace(); } } public static Connection getConn
() { return pool.pop(); } public static void close(Connection connection) { pool.push(connection); }

但是有一個問題,如果當多執行緒連線資料庫時,當超過5個執行緒連線資料庫時,那麼在第6個執行緒包括之後就連線不到資料庫了。 這裡用了java的動態代理來代理資料庫的連線,攔截連線的close方法,來控制資料庫連線池的連線數量,防止多執行緒訪問時出現獲取不到連線的情況,並且給代理的連線加上一個時間屬性,和實時監控的執行緒。初始化5個連線放到棧裡當做資料庫連線池,如果少於5個連線資料庫時,則去呼叫真正的連線方法,且當連線用完時,將連線的時間清零,放回連線池裡。如果大於5個連線資料庫時,連線池沒有連線時,則建立新的連線放進連線池裡,當連線關閉時,如果連線池裡已有5個連線,多餘的連線如果超過10秒沒被使用,則關閉連線。 實現程式碼如下:

package pool;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Stack;

public class PoolServer {
	// 代理連線類
	public class ConnProxy {
		// 連線屬性
		private Connection conn;
		// 用於計時的屬性
		private int idle;

		public int getIdle() {
			return idle;
		}

		public void setIdle(int idle) {
			this.idle = idle;
		}

		public void setConn(Connection conn) {
			this.conn = conn;
		}

		public Connection getConn() {
			return conn;
		}

		//
		public ConnProxy() {
			// 設定空閒時間,如果空閒時間超過10秒,則回收,關閉連線
			new Thread(() -> {
				try {
					while (true) {
						Thread.sleep(1000);
						idle += 1000;
						if (idle > 10000) {
							synchronized (Object.class) {
								if (pool.size() > 5) {
									conn.close();
									pool.remove(ConnProxy.this);
									break;
								}
							}
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}).start();
		}
	}

	// 使用棧定義資料庫連線池
	private Stack<ConnProxy> pool = new Stack<ConnProxy>();
	// 初始化連線
	{
		try {
			Class.forName("com.mysql.jdbc.Driver");
			for (int i = 0; i < 5; i++) {
				Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dvdstore", "root", "root");
				ConnProxy connProxy = new ConnProxy();
				connProxy.setConn(conn);
				pool.push(connProxy);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 獲取連線
	// 這裡加了鎖,防止在多執行緒情況下,會同時去獲取連線導致空棧
	public synchronized Connection getConn() {
		// 如果資料庫池裡沒連線的時候,則建立新的連線放入資料庫連線池
		if (pool.isEmpty()) {
			for (int i = 0; i < 3; i++) {
				try {
					Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dvdstore", "root",
							"root");
					ConnProxy connProxy = new ConnProxy();
					connProxy.setConn(conn);
					pool.push(connProxy);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		// ***
		// 有連線,則從棧裡獲取連線
		final ConnProxy p = pool.pop();
		// 使用java動態代理來代理連線類,攔截close這個方法
		return (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), new Class[] { Connection.class },
				new InvocationHandler() {
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						synchronized (Object.class) {
							// 當連線close則將連線重新放回資料庫連線池
							if (method.getName().equals("close")) {
								p.setIdle(0);// 被使用過的idle從0開始

								pool.push(p);

								System.out.println(pool.size());
								return null;// 不讓其呼叫真正的close方法
							} else {// 當connect不是執行close方法,而是執行其他操作,例如增刪改查等操作
								Connection conn = p.getConn();
								return method.invoke(conn, args);// 呼叫其真正的connetion方法
							}
						}
					}
				});
	}

	// 測試
	public static void main(String[] args) throws Exception {
		final PoolServer poolServer = new PoolServer();

		for (int i = 0; i < 5; i++) {
			new Thread(()-> {
					try {
						Connection conn = poolServer.getConn();
						conn.close();
						// System.out.println(conn);
						// conn.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
			}).start();
		}
		System.in.read();
	}
}