04.手寫資料庫連線池
阿新 • • 發佈:2019-01-22
1. 設計思路
-
在內部物件池中,維護一定數量的資料庫連線,並對外暴露資料庫連線的獲取和返回方法。
-
如外部使用者可通過 getConnection 方法獲取資料庫連線,使用完畢後再通過 releaseConnection 方法將連線返回,注意此時的連線並沒有關閉,而是由連線池管理器回收,併為下一次使用做好準備。
2. pom.xml
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId> mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version >
</dependency>
</dependencies>
3. 程式碼
3.1 DBProperties
@Data
public class DBProperties {
private String driverName = "com.mysql.jdbc.Driver";
private String url = "jdbc:mysql://localhost:3306/mp?useSSL=false";
private String userName = "root";
private String password = "root";
/**
* 連線池名字
*/
private String poolName = "database-pool-";
/**
* 空閒池,最小連線數
*/
private int minConnections = 5;
/**
* 空閒池,最大連線數
*/
private int maxConnections = 10;
/**
* 初始化連線數
*/
private int initConnections = 2;
/**
* 重複獲得連線的頻率
*/
private long connTimeOut = 1000;
/**
* 連線超時時間,預設20分鐘
*/
private long connectionTimeOut = 1000 * 60 * 20;
}
3.2 ConnectionPoolManager
public class ConnectionPoolManager {
/**
* 空閒連線集合
*/
private List<Connection> freeConnection = new Vector<>();
/**
* 活動連線集合
*/
private List<Connection> activeConnection = new Vector<>();
/**
* 連線數
*/
private AtomicInteger connectCount = new AtomicInteger(0);
/**
* 資料庫配置資訊
*/
private DBProperties dbProperties = new DBProperties();
public ConnectionPoolManager() {
this.init();
}
/**
* 獲取連線
*
* @return
* @throws Exception
*/
public synchronized Connection getConnection() throws Exception {
Connection connection = null;
if (activeConnection.size() >= dbProperties.getMaxConnections()) {
//如果達到了最大連線,則等待 connTimeOut
System.out.println("當前活動連線已經達到了最大連線:" + activeConnection.size() + ",等待ing");
wait(dbProperties.getConnectionTimeOut());
connection = this.getConnection();
} else {
//如果未達到最大連線數,則返回 connection
if (freeConnection.size() > 0) {
//如果有空閒連線
System.out.println("當前空閒連線還有:" + freeConnection.size() + ",從空餘連線池中獲取連線");
connection = freeConnection.remove(0);
} else {
//沒有空閒連線
connection = this.newConnection();
System.out.println("當前沒有空閒連線,利用 JDBC 技術獲取連線");
}
activeConnection.add(connection);
}
return connection;
}
/**
* 釋放連線
*
* @param connection
* @throws Exception
*/
public synchronized void releaseConnection(Connection connection) throws Exception {
if (!isAvailable(connection)) {
return;
}
if (freeConnection.size() <= dbProperties.getMinConnections()) {
//如果當前連線小於最小連線數
System.out.println("當前空閒連線還有:" + freeConnection.size() + ",不大於 minConnections 配置:" + dbProperties.getMinConnections() + ",重新放入到空閒連線池中");
freeConnection.add(connection);
} else {
//如果大於最小連線數,則關閉
connection.close();
this.connectCount.decrementAndGet();
System.out.println("當前空閒連線還有:" + freeConnection.size() + ",大於 minConnections 配置:" + dbProperties.getMinConnections() + ",銷燬連線");
}
activeConnection.remove(connection);
notifyAll();
}
/**
* 初始化連線
*/
private void init() {
Connection connection = null;
for (int i = 0; i < dbProperties.getInitConnections(); i++) {
connection = this.newConnection();
freeConnection.add(connection);
}
System.out.println("資料庫連線池初始化完成:共有 " + freeConnection.size() + " 個連線");
}
/**
* jdbc 獲取連線
*
* @return
*/
private Connection newConnection() {
try {
if (dbProperties == null) {
return null;
}
Class.forName(dbProperties.getDriverName());
Connection connection = DriverManager.getConnection(dbProperties.getUrl(), dbProperties.getUserName(), dbProperties.getPassword());
connectCount.incrementAndGet();
System.out.println("從資料庫中獲取連線:" + connection + ",當前連線數:" + connectCount);
return connection;
} catch (Exception e) {
return null;
}
}
/**
* 判斷連線是否可用
*
* @param connection
* @return
*/
private boolean isAvailable(Connection connection) {
try {
if (connection == null || connection.isClosed()) {
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
4. 測試
public class TestThreadPool {
public static void main(String[] args) {
final ConnectionPoolManager connectionPoolManager = new ConnectionPoolManager();
for (int i = 0; i < 15; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
for (int y = 0; y < 10; y++) {
Connection connection = connectionPoolManager.getConnection();
Thread.sleep(1);
connectionPoolManager.releaseConnection(connection);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, "使用者執行緒:" + i).start();
}
}
}