自己實現一個mini的數據庫連接池
注意點:
- 數據庫連接池中存放的就是數據庫操作管道,不僅僅是存放,而且應該是管理這些管道;
- 應該提供外部配置文件去初始化數據庫連接池;
- 如果一個數據庫操作管道已經被佔用,那麼其他請求是否應該得到這個管道,也就是說我們要考慮多線程併發下,管道的分配問題;
- 如果做到管道的複用?放回池子中,標示可用,並不是真正的關閉管道;
/**
* @ClassName XML
* @Description 代表xml,配置文件類
* @Author lzq
* @Date 2019/7/8 02:10
* @Version 1.0
**/
public class XML {
public static final String Driver = "com.mysql.jdbc.Driver";
public static final String Url = "jdbc:mysql://127.0.0.1:3306/test4";
public static final String User = "root";
public static final String Password = "123456";
//數據庫連接池初始化大小
public static final int initCount = 2;
//連接池連接不足的時候新創建的連接數量
public static final int step = 2;
//連接池的最大數量
public static final int maxCount = 10;
public static final int maxTime = 200;
}
import java.sql.Connection;
/**
* @ClassName GovernConnection
* @Description 管理連接通道的類
* @Author lzq
* @Date 2019/7/8 02:18
* @Version 1.0
**/
public class GovernConnection {
private Connection connection;
private boolean sign = false; //標記通道是否被佔用 false:沒有被佔用
private long startTime = 0;
public GovernConnection(Connection connection,boolean sign,long startTime) {
this.connection = connection;
this.sign = sign;
this.startTime = startTime;
}
public void setSign(boolean sign) {
this.sign = sign;
}
public boolean isSign() {
return sign;
}
public Connection getConnection() {
return connection;
}
public void setConnection(Connection connection) {
this.connection = connection;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public long getStartTime() {
return startTime;
}
/**
* 關閉通道,那麼標記這個通道,以示其他線程可以用了
*/
public void close() {
this.sign = false;
setStartTime(System.currentTimeMillis());
}
}
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassName MyConnectionPool
* @Description 數據庫連接池
* 加載驅動、初始化變量
* 創建、獲取連接
* 用集合存儲連接
* @Author lzq
* @Date 2019/7/8 02:20
* @Version 1.0
**/
public class MyConnectionPool {
private ReentrantLock lock = new ReentrantLock();
private List<GovernConnection> myPool = new ArrayList<>();
private List<GovernConnection> remove = new ArrayList<>();
private static String url;
private static String user;
private static String password;
private static int initCount;
private static int step;
private static int maxCount;
private static int maxTime;
public MyConnectionPool() {
//初始化參數
init();
//加載驅動
try {
Class.forName(XML.Driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 初始化連接池配置
*/
private void init() {
url = XML.Url;
user = XML.User;
password = XML.Password;
initCount = XML.initCount;
step = XML.step;
maxCount = XML.maxCount;
maxTime = XML.maxTime;
}
/**
* 創建操作通道的類
* @param count
*/
private synchronized void createConnection(int count) {
count = count+myPool.size() < maxCount ? count : maxCount-myPool.size();
for (int i = 0; i < count; i++) {
try {
Connection connection = DriverManager.getConnection(url,user,password);
GovernConnection governConnection = new GovernConnection(connection,false,System.currentTimeMillis());
myPool.add(governConnection);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 獲取操作通道的類
* @return
*/
public GovernConnection getConnection() {
GovernConnection governConnection = null;
try {
lock.lock();
if(myPool.size() < 1) {
//初始化指定數量的數據庫連接池管道
createConnection(initCount);
}
governConnection = getGovernConnection();
while (governConnection == null) {
System.out.println("創建新鏈接");
createConnection(step);
governConnection = getGovernConnection();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return governConnection;
}
/**
* 在連接池裏面找一個未被使用的連接
* @return
*/
private GovernConnection getGovernConnection() throws SQLException {
boolean flag = false;
Iterator<GovernConnection> iterator = myPool.iterator();
while (iterator.hasNext()) {
GovernConnection governConnection = iterator.next();
Connection connection = governConnection.getConnection();
//如果當前通道已經關閉或超時,在連接池中去除它
if(connection.isClosed() || System.currentTimeMillis()-governConnection.getStartTime() > maxTime) {
System.out.println("出現關閉或超時的連接");
remove.add(governConnection);
flag = true;
}
//未超時,也未被佔用
else if(!governConnection.isSign()) {
governConnection.setStartTime(System.currentTimeMillis());
governConnection.setSign(true);
return governConnection;
}
}
if(flag) {
remove();
}
return null;
}
/**
* 刪除超時的和關閉的連接
* 但不能刪除到比最小連接數還小(剩下那部分重置)
*/
private void remove() {
int removeCount = remove.size();
if(myPool.size()-removeCount < initCount) {
delete(myPool.size()-initCount);
reset();
}else {
delete(myPool.size()-removeCount);
}
}
/**
* 刪除指定個數連接
* @param count
*/
private void delete(int count) {
Iterator<GovernConnection> iterator = remove.iterator();
while (iterator.hasNext() && count > 0) {
GovernConnection governConnection = iterator.next();
myPool.remove(governConnection);
iterator.remove();
count--;
}
}
/**
* 重置連接
*/
private void reset() {
Iterator<GovernConnection> iterator = remove.iterator();
while (iterator.hasNext()) {
GovernConnection governConnection = iterator.next();
governConnection.setStartTime(System.currentTimeMillis());
governConnection.setSign(false);
iterator.remove();
}
}
/**
* 打印一下現存的連接
*/
public void show() {
for (GovernConnection governConnection: myPool) {
System.out.println(governConnection+"\t"+governConnection.getConnection());
}
System.out.println(myPool.size());
}
}
/**
* @ClassName ConnectionsPool
* @Description 獲取連接池的單例對象
* @Author lzq
* @Date 2019/7/8 02:23
* @Version 1.0
**/
enum MyConnectionsPool {
MY_POOL;
private MyConnectionPool myPool;
MyConnectionsPool() {
this.myPool = new MyConnectionPool();
}
public MyConnectionPool getMyDBPool() {
return myPool;
}
}
public class ConnectionsPool {
public static MyConnectionPool getMyConnectionPool() {
return MyConnectionsPool.MY_POOL.getMyDBPool();
}
}
import java.sql.Connection;
/**
* @ClassName TestDemo10
* @Description 測試類
* @Author lzq
* @Date 2019/7/8 02:54
* @Version 1.0
**/
public class TestDemo10 {
public static void main(String[] args) throws InterruptedException {
MyConnectionPool myConnectionPool = ConnectionsPool.getMyConnectionPool();
show2(myConnectionPool);
}
private static void show2(MyConnectionPool myConnectionPool) throws InterruptedException {
TestDemo10 demo10 = new TestDemo10();
for (int i = 0; i < 50; i++) {
TestDemo10.MyTestThread myTestThread = demo10.new MyTestThread(myConnectionPool);
myTestThread.start();
myTestThread.join();
}
myConnectionPool.show();
}
private class MyTestThread extends Thread {
private MyConnectionPool myConnectionPool;
public MyTestThread(MyConnectionPool myConnectionPool) {
this.myConnectionPool = myConnectionPool;
}
@Override
public void run() {
GovernConnection governConnection = myConnectionPool.getConnection();
for (int i = 0; i < 5; i++) {
String sql = "select * from student";
Connection connection = governConnection.getConnection();
}
System.out.println(Thread.currentThread().getName()+"\t"+governConnection
+"\t"+governConnection.getConnection());
governConnection.close();
}
}
}
運行結果:
我這個連接池裏面封裝了一個GovernConnection類,它的作用是管理連接,並記錄當前連接是否已經被佔用,以及它的起始時間(後面用來判斷是否需要超時回收的),雖然讓邏輯簡單了一點,但是它卻增加了學習成本,因爲像c3p0、dbcp、druid這些連接池返回的直接是一個Connection對象,然後操作這個Connection對象的,但是思想還是可以借鑑的,你只需要連接池就是這麼個原理就好了。
那麼還有一種實現連接池的方式是,定義兩個集合存放連接,其中一個存放的連接是不能被回收的(它存放的是連接池最小連接數的連接,也就是連接池的連接數不能比這個還小了),對於它裏面的超時連接,只能重置,而另一個集合存放的連接是可以被超時回收的,也就是大於連接池最小存在數而小於最大存在數的連接全放在它裏面,然後其他操作與以上代碼思想類似;