一、前言

  话说在Java开发中,连接总是是必不可少的。有数据库连接、http连接等。面试内容也会经常问起(虽然我没被面到过:)。那究竟什么是连接池呢?它有什么作用?又是怎么实现的呢?

二、什么和为什么?

  常见连接的代码通常要经历建立连接、处理业务和关闭连接三个步骤。伪代码如下:



Connection cnn =null;
try{
cnn = getConnection();
cnn.excute();
}finally{
cnn.close();
}


这种情况在少数并发情况下,简单实用。可一旦面临多并发情况下就显得有些不够用了。这里的瓶颈在于每次都需要创建连接和关闭连接,这种其实重复且耗时。比如mysql其实是会缓存常用的连接,达到性能优化的效果,如果应用端频繁关闭和新建连接,那其实就用不到这种优化了。如果能复用连接,预先创建连接池,使用时拿取一个,用完再归还,从而减少创建和关闭的消耗,那必然是能达到优化的效果了。而这就是为什么采用连接池的原因了。

  那至于什么是连接池?那根据上面的描述不难得出,连接池必然有以下元素:1、连接集合 2、获取连接 3、释放(归还)连接。只要具备这三样基本就能称之为连接池了。不会动手的程序猿,不是一个好攻城狮。接下来就动手试试?

三、实验

  本次实验就以Java的mysql连接为例,测试普通连接方式和连接池方式的性能吧。

  JdbcCnnPool类:



public class JdbcCnnPool {

private static final Integer poolSize = 20;

private static final ReentrantLock getLock = new ReentrantLock();

private static final ReentrantLock releaseLock = new ReentrantLock();

private static final List<Connection> cnnList = new ArrayList<>();


static public Connection getConnection() throws Exception {
try {
getLock.lock();
if (CollectionUtils.isEmpty(cnnList)) {
for (int i = 0; i < poolSize; i++) {
String URL = "jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf-8";
String USER = "root";
String PASSWORD = "root1234";
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库链接
Connection cnn = DriverManager.getConnection(URL, USER, PASSWORD);
cnnList.add(cnn);
}
}
return cnnList.remove(0);
} catch (Exception e) {
throw e;
} finally {
getLock.unlock();
}
}

static public void releaseConnection(Connection connection) {
try {
releaseLock.lock();
if (cnnList.size() <= poolSize) {
cnnList.add(connection);
}
} catch (Exception e) {

} finally {
releaseLock.unlock();
}
}

}


测试类:



public class ConnectionTest {

@Test
public void connectionTest() throws Exception {

int num = 100;
newConnectionTest(num);
connectionPoolTest(num);
}


private void newConnectionTest(int num) throws Exception {
Long startTime = System.currentTimeMillis();
for (int i = 0; i < num; i++) {
String URL = "jdbc:mysql://127.0.0.1:3306/db1?useUnicode=true&characterEncoding=utf-8";
String USER = "root";
String PASSWORD = "root1234";
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库链接
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("select * from user");
//4.处理数据库的返回结果(使用ResultSet类)
while (rs.next()) {
// System.out.println(rs.getString("name") + " "
// + rs.getString("pwd"));
}
//关闭资源
rs.close();
st.close();
conn.close();
}
Long endTime = System.currentTimeMillis();
System.out.println("newConnectionTest " + (endTime - startTime));

}

private void connectionPoolTest(int num) throws Exception {
Long startTime = System.currentTimeMillis();
for (int i = 0; i < num; i++) {
Connection conn = JdbcCnnPool.getConnection();
try {
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("select * from user");
while (rs.next()) {
// System.out.println(rs.getString("name") + " "
// + rs.getString("pwd"));
}

} catch (Exception e) {

} finally {
JdbcCnnPool.releaseConnection(conn);
}

}
Long endTime = System.currentTimeMillis();
System.out.println("connectionPoolTest " + (endTime - startTime));

}

}


实验结果:

1、100次查询(连接数据库)

正常连接执行时间:2553

连接池执行时间:322

2、1000次查询

正常连接执行时间:8376

连接池执行时间:356

3、10000次查询

正常连接执行时间:64627

连接池执行时间:1407

基本可以看出,连接池的方式的性能是正常的10倍以上,所以连接池还是很有用的。当然基本现在的连接工具基本都是采用连接池的,比如druid、c3p0、tomcat-jdbc,理解连接池的基本原理也能更好的帮助理解源码了。

四、总结

   连接池是个好东西,生产上能用则用。多看轮子,多造小轮子,为了造更好的轮子!