参照《并发编程的艺术》4.4.2 设计自己的数据库连接池,首先一个空闲列表,一个活动连接链表,初始的时候,都是空的,如果为空,DriverManager获得connection .放入到活动链表中,然后完成事物,就从活动链表中删除,放入空闲链表中。假如空闲列表是不为空,获取connection是优先从空闲链表中获取。放入活动连接的时候是put操作,具有阻塞特性,避免数据库占用太多连接。
上代码:
public class DBUtil {
//数据库驱动
private static String driver = "com.mysql.jdbc.Driver";
//数据库的资源地址
private static String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8";
//用户名
private static String userName = "root";
//密码
private static String password = "";
public static Connection getConnection() {
try {
//加载驱动
Class.forName(driver);
return DriverManager.getConnection(url, userName, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//连接关闭
public static void closeConn(ResultSet rs, PreparedStatement ps, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
代理连接的回调函数,ConnectionInvocationHandler,拦截close方法,吧当前connection从活动列表中删除,然后加入空闲链表。
public class ConnectionInvocationHandler implements InvocationHandler {
private Connection originalConn;
private LinkedBlockingQueue activeList;
private LinkedBlockingQueue idleList;
public ConnectionInvocationHandler(Connection conn, LinkedBlockingQueue activeList, LinkedBlockingQueue idleList) {
this.originalConn = conn;
this.activeList = activeList;
this.idleList = idleList;
}
@Override
public Object invoke(Object obj, Method method, Object[] args) throws Exception {
if ("close".equals(method.getName())) {
idleList.put(originalConn);
activeList.remove(originalConn);
System.out.println("空闲连接数量: " + (idleList == null ? 0 : idleList.size()));
System.out.println("活动连接数量: " + (activeList == null ? 0 : activeList.size()));
return null;
} else
{
//如果不是close方法,则继续执行目标的行为
return method.invoke(originalConn, args);
}
}
}
连接池:
public class ConnectionPool {
private static final int POOL_SIZE = 20;
private volatile static int connectionCount = 0;
public static LinkedBlockingQueue idleList = new LinkedBlockingQueue<>(POOL_SIZE);
public static LinkedBlockingQueue activeList = new LinkedBlockingQueue<>(POOL_SIZE);
//刚开始的时候,就给他20个空闲的连接
static {
for(int i=0;i
测试代码:
import java.sql.Connection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class ConnectionPoolTest {
static ConnectionPool pool = new ConnectionPool();
// 保证所有ConnectionRunner能够同时开始
static CountDownLatch start = new CountDownLatch(1);
// main线程将会等待所有ConnectionRunner结束后才能继续执行
static CountDownLatch end;
public static void main(String[] args) throws Exception {
// 线程数量,可以修改线程数量进行观察
int threadCount = 300;
//查看mysql show status like '%connect%'; Max_used_connections,服务器启动后已经同时使用的连接的最大数量。
end = new CountDownLatch(threadCount);
int count = 20;
AtomicInteger got = new AtomicInteger();
AtomicInteger notGot = new AtomicInteger();
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new ConnetionRunner(count, got, notGot),
"ConnectionRunnerThread");
thread.start();
}
start.countDown();
long start = System.currentTimeMillis();
end.await();
System.out.println("total invoke: " + (threadCount * count));
System.out.println("got connection: " + got);
System.out.println("not got connection " + notGot);
System.out.println("time elapsed ===== " + (System.currentTimeMillis()-start)+"ms");
}
static class ConnetionRunner implements Runnable {
int count;
AtomicInteger got;
AtomicInteger notGot;
public ConnetionRunner(int count, AtomicInteger got, AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
public void run() {
try {
start.await();
} catch (Exception ex) {
}
while (count > 0) {
try {
// 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
// 分别统计连接获取的数量got和未获取到的数量notGot
Connection connection = ConnectionPool.getConnection();
if (connection != null) {
try {
Statement statement = connection.createStatement();
statement.executeQuery("select * from user;");
} finally {
got.incrementAndGet();
connection.close();
}
}else{
notGot.incrementAndGet();
}
} catch (Exception ex) {
notGot.incrementAndGet();
} finally {
count--;
}}
end.countDown();
}
}
}
最后输出结果:
total invoke: 6000
got connection: 6000
not got connection 0
time elapsed ===== 2520ms
当大于上面的201时,数据库瞬间报错,但是后面的有cpu线程调度之后,就没有报错了。可能线程启动的瞬间都连接数据库,压力突然增大。但是后面通过两个阻塞链表,来限制连接。
mybatis的PoolDataSource
上测试代码:
public class PooledDataSourceTest {
static PooledDataSource pool = new PooledDataSource(DBUtil.driver,DBUtil.url,DBUtil.userName,DBUtil.password);
static CountDownLatch start = new CountDownLatch(1);
static CountDownLatch end;
public static void main(String[] args) throws Exception {
// 线程数量,可以修改线程数量进行观察
int threadCount = 200; //当调整参数到201以上,就会报错,【too many connections】
//查看mysql show status like '%connect%'; Max_used_connections,服务器启动后已经同时使用的连接的最大数量。
end = new CountDownLatch(threadCount);
int count = 20;
AtomicInteger got = new AtomicInteger();
AtomicInteger notGot = new AtomicInteger();
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new PooledDataSourceTest.ConnetionRunner(count, got, notGot),
"ConnectionRunnerThread");
thread.start();
}
System.out.println("线程启动完成");
start.countDown();
long start = System.currentTimeMillis();
end.await();
System.out.println("total invoke: " + (threadCount * count));
System.out.println("got connection: " + got);
System.out.println("not got connection " + notGot);
System.out.println("time elapsed ===== " + (System.currentTimeMillis()-start)+"ms");
}
static class ConnetionRunner implements Runnable {
int count;
AtomicInteger got;
AtomicInteger notGot;
public ConnetionRunner(int count, AtomicInteger got, AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
public void run() {
System.out.println("进入线程");
try {
start.await();
} catch (Exception ex) {
}
while (count > 0) {
try {
Connection connection = pool.getConnection();
System.out.println("获取连接");
if (connection != null) {
try {
Statement statement = connection.createStatement();
statement.executeQuery("select * from user;");
} finally {
got.incrementAndGet();
connection.close();
System.out.println("释放连接");
}
}
} catch (Exception ex) {
ex.printStackTrace();
notGot.incrementAndGet();
} finally {
count--;
}}
end.countDown();
}
}
}
测试结果:
total invoke: 6000
got connection: 6000
not got connection 0
time elapsed ===== 3652ms