连接池的一次实验

一、前言

  话说在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,理解连接池的基本原理也能更好的帮助理解源码了。

四、总结

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

posted @ 2021-07-17 20:46  程序实验室  阅读(76)  评论(0)    收藏  举报