自定义数据库连接池 VS mybatis内部的PooledDataSource

参照《并发编程的艺术》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
image.png

当大于上面的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

你可能感兴趣的:(自定义数据库连接池 VS mybatis内部的PooledDataSource)