針對 App 服務端高併發訪問 MySQL 的場景,本文提供一個輕量級、線程安全的連接池實現,採用雙重檢查鎖單例模式、HashMap 管理連接狀態、阻塞等待擴容策略,經 1 萬併發壓測 5 秒完成,驗證可用性。代碼結構清晰,適合二次開發或教學參考。
volatile + 雙重檢查鎖,確保多線程環境下唯一實例。Map<Connection, Boolean>,true=空閒,false=使用中。initPoolSize,上限 maxPoolSize,耗盡時 wait() 退避重試。isClosed(),歸還時重置 autoCommit=true,異常連接自動剔除。finally 塊確保 ResultSet/Statement/Connection 正確關閉/歸池。MySQLPoolpackage com.junones.test;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
public class MySQLPool {
private static volatile MySQLPool pool;
private MysqlDataSource ds;
private Map<Connection, Boolean> map;
private String url = "jdbc:mysql://localhost:3306/test";
private String username = "root";
private String password = "root1234";
private int initPoolSize = 10;
private int maxPoolSize = 200;
private int waitTime = 100; // ms
private MySQLPool() { init(); }
public static MySQLPool getInstance() {
if (pool == null) {
synchronized (MySQLPool.class) {
if (pool == null) pool = new MySQLPool();
}
}
return pool;
}
private void init() {
try {
ds = new MysqlDataSource();
ds.setUrl(url);
ds.setUser(username);
ds.setPassword(password);
ds.setCacheCallableStmts(true);
ds.setConnectTimeout(1000);
ds.setLoginTimeout(2000);
ds.setUseUnicode(true);
ds.setEncoding("UTF-8");
ds.setZeroDateTimeBehavior("convertToNull");
ds.setMaxReconnects(5);
ds.setAutoReconnect(true);
map = new HashMap<Connection, Boolean>();
for (int i = 0; i < initPoolSize; i++) {
map.put(getNewConnection(), true);
}
} catch (Exception e) { e.printStackTrace(); }
}
public Connection getNewConnection() {
try { return ds.getConnection(); }
catch (SQLException e) { e.printStackTrace(); }
return null;
}
public synchronized Connection getConnection() {
Connection conn = null;
try {
for (Entry<Connection, Boolean> entry : map.entrySet()) {
if (entry.getValue()) {
conn = entry.getKey();
map.put(conn, false);
break;
}
}
if (conn == null) {
if (map.size() < maxPoolSize) {
conn = getNewConnection();
map.put(conn, false);
} else {
wait(waitTime);
conn = getConnection(); // 遞歸重試
}
}
} catch (Exception e) { e.printStackTrace(); }
return conn;
}
public void releaseConnection(Connection conn) {
if (conn == null) return;
try {
if (map.containsKey(conn)) {
if (conn.isClosed()) map.remove(conn);
else {
if (!conn.getAutoCommit()) conn.setAutoCommit(true);
map.put(conn, true);
}
} else { conn.close(); }
} catch (SQLException e) { e.printStackTrace(); }
}
}
TestMySQLPoolpackage com.junones.test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestMySQLPool {
private static volatile int a;
private synchronized static void incr() { a++; }
public static void main(String[] args) throws InterruptedException {
int times = 10000;
long start = System.currentTimeMillis();
for (int i = 0; i < times; i++) {
new Thread(() -> {
MySQLPool pool = MySQLPool.getInstance();
Connection conn = pool.getConnection();
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
rs = stmt.executeQuery("select id, name from t_test");
while (rs.next()) {
System.out.println(rs.getInt(1) + ", " + rs.getString(2));
}
} catch (SQLException e) { e.printStackTrace(); }
finally {
incr();
try { if (rs != null) rs.close(); } catch (SQLException ignore) {}
try { if (stmt != null) stmt.close(); } catch (SQLException ignore) {}
pool.releaseConnection(conn);
}
}).start();
}
while (true) {
if (a == times) {
System.out.println("finished, time:" + (System.currentTimeMillis() - start));
break;
}
Thread.sleep(100);
}
}
}
測試環境:1 萬併發線程,單表簡單查詢,5 秒完成,吞吐約 2000 QPS。
已知局限與生產級替代方向:
HashMap 非線程安全遍歷,高併發下建議改用 ConcurrentLinkedQueue<PooledConnection> + AtomicInteger 計數器。wait()/notify() 低級同步原語,易發生虛假喚醒,推薦 ReentrantLock + Condition 或信號量 Semaphore。validationQuery)、空閒回收、洩漏檢測、JMX 監控指標。手寫連接池是理解資源池化原理的最佳途徑,但生產系統請站在巨人肩膀上。本文代碼僅供教學與輕量級項目參考,核心邏輯可移植至其他語言實現。