用Java实现简单的数据库连接池

来源:互联网 发布:淘宝网店打底衫长袖 编辑:程序博客网 时间:2024/05/24 06:59

1 引言

对于数据库连接池,相信大家都不会陌生,当下流行的连接池也有很多(如:DBCP,C3P0等),使用连接池可以在程序中给我们带来极大的方便,也可以有效的减少创建连接给我们带来的开销,前段时间公司的项目中有用到DBCP连接池,所以在闲暇之余,自己也尝试着写了一个简易数据库连接池,当然如果对数据库连接池不是很懂的话,建议参考Connection Pooling with Connector/J ,详细的介绍了连接池这项技术。

2 概述

下面介绍的这个简易连接池主要包含了一下内容和功能:

  1. 获取连接
  2. 回收连接
  3. 验证连接的有效性(获取连接前验证)
  4. 定期维护连接
  5. 当前连接耗尽后自动增加连接数

3 源码

import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.SQLException;import java.util.HashMap;import java.util.Map;import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.atomic.AtomicInteger;import org.apache.log4j.Logger;public class DBPoolUtils {    private static Logger logger = Logger.getLogger(DBPoolUtils.class);    /*     * Map<Connection, String("last_call_time"-"create_time")>     *      * 当连接超过设定生命周期,或者大于设定的空闲时间则该链接自动销毁     * 本例没有实现生命周期和空闲时间管理,但是数据结构已经构造好了,需要进行生命周期管理的话,可以在     * release()方法回收时进行判断,if((当前时间-创建时间)>life_cycle_time){销毁连接} 如果不做生命周期管理,pool     * 可设计为LinkedBlockingQueue<Connection> ,不需要borrowed_queue。     */    private static LinkedBlockingQueue<Map<Connection, String>> pool = new LinkedBlockingQueue<>();    /*     * Map<Connection,create_time>     */    private static Map<Connection, Long> borrowed_queue = new HashMap<>();    private Integer init_pool_size = 10;    private static Integer maximum_pool_size = 21;    /*     * 计数器,记录当前已生成连接数     */    private static AtomicInteger countor = new AtomicInteger(0);    /*     * 定期检查:最大空闲连接数,超过时,对多余连接进行回收     */    private Integer max_idel = 30;    /*     * 定期检查:当当前空闲连接数,小于最小连接数时,新增连接,保持最小空闲连接数     */    private Integer min_idel = 5;    private String driver_class_name = "com.mysql.jdbc.Driver";    private String driver_url = "jdbc:mysql://127.0.0.1:3306/dbname";    private String driver_user = "user";    private String driver_password = "password";    /*     * 使用ping可以进行快速验证     */    private static final String validation_Query = "/* ping */ SELECT 1";    /*     * 1小时 毫秒数,默认连接池检测周期     */    private long default_check_period = 60 * 60 * 1000;    /*     * 验证连接时超时时间,当连接超时时,自动生成新连接     */    private Integer validation_Query_Timeout = 10;    // 在获取连接时验证    private boolean test_on_borrow = false;    // 每次新增连接条数    private Integer increase_number = 5;    private DBPoolUtils() {        // 初始化之前先配置数据        init();        // 设置周期性检测空闲连接        schedule_check();    }    private static class InstanceHolder {        static DBPoolUtils instance = new DBPoolUtils();    }    public static DBPoolUtils getInstance() {        return InstanceHolder.instance;    }    /**     * 获取连接     *      * @return     */    public Connection getConnection() {        Map<Connection, String> connection_map = new HashMap<>();        Connection connecton = null;        Long create_time = 0L;        try {            connection_map = pool.poll();            // 当连接池连接已经耗尽之后重新创建新的连接            if (connection_map == null && countor.get() < maximum_pool_size) {                // 多重检查                if (pool.size() == 0 && countor.get() < maximum_pool_size) {                    increase();                }                connection_map = pool.take();            }            connecton = (Connection) connection_map.keySet().toArray()[0];            create_time = Long.valueOf(connection_map.get(connecton).split("-")[1]);            borrowed_queue.put(connecton, create_time);        } catch (Exception e) {        }        // 在借出连接时检查连接是否可用        if (test_on_borrow) {            boolean states = validation_Query(connecton);            connecton = states ? connecton : getConnection();        }        return connecton;    }    /**     * 检测连接是否可用     *      * @param con     * @return     */    private boolean validation_Query(Connection con) {        boolean tag = false;        PreparedStatement statement = null;        try {            statement = con.prepareCall(validation_Query);            statement.setQueryTimeout(validation_Query_Timeout);            statement.executeQuery();            tag = true;        } catch (SQLException e) {            tag = false;            countor.incrementAndGet();            if (con != null)                try {                    con.close();                } catch (SQLException e1) {                    logger.error(e1.getMessage(), e1);                }            logger.error(e.getMessage(), e);        } finally {            if (statement != null)                try {                    statement.close();                } catch (SQLException e) {                    logger.error(e.getMessage(), e);                }        }        return tag;    }    /**     * 初始化连接     */    private synchronized void init() {        Connection conn;        try {            Class.forName(driver_class_name);            while (init_pool_size-- > 0) {                conn = DriverManager.getConnection(driver_url, driver_user, driver_password);                Map<Connection, String> map = new HashMap<>();                String time_str = new StringBuilder(String.valueOf(System.currentTimeMillis())).append("-")                        .append(String.valueOf(System.currentTimeMillis())).toString();                map.put(conn, time_str);                pool.put(map);                countor.incrementAndGet();            }        } catch (ClassNotFoundException e) {            logger.error("Failed to Load \"driver_class_name\" ,int init() method\n" + e.getMessage(), e);        } catch (SQLException e) {            logger.error(                    "Failed to get connection to database ,please check configure file!if thest configure was correct configured!\n"                            + e.getMessage(),                    e);        } catch (InterruptedException e) {            logger.error("Failed to put connection to pool,please check data structure!\n" + e.getMessage(), e);        }    }    /**     * 连接回收     *      * @param connection     */    public void release(Connection connection) {        /*         * 使用一条线程来处理回收,主要是考虑到回收的效率问题,但这样可能会存在另外一个问题         * 已经释放掉的连接,未被及时放入线程池中         * 但是这样的处理方式,可以加快程序的反应速度,具体影响待测,当然理论上只要逻辑没有问         * 题,是不会产生什么实际影响。         */        new Thread(new Runnable() {            @Override            public void run() {                try {                    if (connection != null) {                        long create_time = borrowed_queue.remove(connection);                        String time_str = new StringBuilder(String.valueOf(System.currentTimeMillis())).append("-")                                .append(String.valueOf(create_time)).toString();                        Map<Connection, String> map = new HashMap<>();                        map.put(connection, time_str);                        pool.put(map);                        System.out.println("重入连接池....");                    }                } catch (InterruptedException e) {                    logger.info(e.getMessage(), e);                }            }        }).start();    }    /**     * 连接池耗尽时,若未达最大连接数,则增加连接。     */    private synchronized void increase() {        // 检测,如果前一个进入该方法的线程已经产生了新连接,则不做处理,否则产生因连接        if (pool.size() == 0) {            // 增加新连接            Integer current_number = 0;            /*             * 用于记录可增加的数量,如果该数量大于增长量,则用while增加,否则使用指定数量的另一个方法增加             */            Integer tag_number = maximum_pool_size - countor.get();            if (tag_number >= increase_number)                while (countor.get() <= maximum_pool_size && current_number++ < increase_number) {                    try {                        Connection conn;                        conn = DriverManager.getConnection(driver_url, driver_user, driver_password);                        Map<Connection, String> map = new HashMap<>();                        String time_str = new StringBuilder(String.valueOf(System.currentTimeMillis())).append("-")                                .append(String.valueOf(System.currentTimeMillis())).toString();                        map.put(conn, time_str);                        pool.put(map);                        countor.incrementAndGet();                    } catch (SQLException e) {                        logger.error("Failed to increase connections:\t" + e.getMessage(), e);                    } catch (InterruptedException e) {                        logger.error("Failed to put connection into pool:\t" + e.getMessage(), e);                    }                }            else                increase(tag_number - 1);        }    }    /**     * 新增指定条数的连接     *      * @param increase_number     */    private synchronized void increase(Integer increase_number) {        // 增加新连接        while (increase_number-- > 0) {            Connection conn;            try {                conn = DriverManager.getConnection(driver_url, driver_user, driver_password);                Map<Connection, String> map = new HashMap<>();                String time_str = new StringBuilder(String.valueOf(System.currentTimeMillis())).append("-")                        .append(String.valueOf(System.currentTimeMillis())).toString();                map.put(conn, time_str);                pool.put(map);                countor.incrementAndGet();            } catch (SQLException e) {                logger.error("Failed to increase connections:\t" + e.getMessage(), e);            } catch (InterruptedException e) {                logger.error("Failed to put connection into pool:\t" + e.getMessage(), e);            }        }    }    /**     * 销毁所有连接 Close all connections     */    public void close_All_Connections() {        Map<Connection, String> connection_map = new HashMap<>();        connection_map = pool.poll();        int i = 0;        System.out.println(pool.size());        while (connection_map != null) {            Connection con = (Connection) connection_map.keySet().toArray()[0];            try {                if (con != null)                    con.close();            } catch (SQLException e) {                e.printStackTrace();            }            connection_map = pool.poll();            i++;        }        System.out.println("回收完毕" + i);    }    /**     * 检测空闲连接     */    private void check_idel_connection() {        //当前大小即是空闲连接数量        Integer current_idel = pool.size();        Map<Connection, String> connection_map = new HashMap<>();        // 若当前空闲连接数小于最小空闲连接数--新增连接        if (current_idel < min_idel) {            int j = min_idel - current_idel;            increase(j);        } else if (current_idel > max_idel) {            // 若当前空闲连接数大于最大空闲连接数--关闭连接            int i = current_idel - max_idel;            connection_map = pool.poll();            while (i-- > 0) {                if (connection_map != null) {                    Connection con = (Connection) connection_map.keySet().toArray()[0];                    try {                        if (con != null) {                            con.close();                            countor.incrementAndGet();                        }                    } catch (SQLException e) {                        logger.error("Failed to close connection!/t" + e.getMessage(), e);                    }                } else                    break;            }        }    }    /**     * 在指定的时间间隔内检查连接状态     */    private void schedule_check() {        Timer timer = new Timer();        timer.schedule(new TimerTask() {            @Override            public void run() {                check_idel_connection();            }        }, default_check_period, default_check_period);    }}

测试类

public class Ntest {    public static Integer counter=0;    static double start=System.currentTimeMillis();    static double end=0;    public static void main(String[] args) {        int i=100000;        while(i-->0){            new Thread(new Runnable() {                Connection con=null;                @Override                public void run() {                    System.out.println(this.con=DBPoolUtils.getInstance().getConnection());                    DBPoolUtils.getInstance().release(this.con);                    System.out.println("current:"+(++counter));                    if(counter>=9999){                        end=System.currentTimeMillis();                        System.out.println((end-start)/1000.0);                    }                }            }).start();        }    }}

4 测试结果

测试数据:初始化连接数:10条,最大连接数:20条,请求线程数:10W,运行时间:11-14秒(不开启获取连接时进行连接测试)
测试数据:初始化连接数:10条,最大连接数:20条,请求线程数:10W,运行时间:14-20秒 (开启获取连接时验证)

5 总结

这个简易线程池只实现了最基本的提供连接管理的功能(创建回收),稍微改善程序可以在此基础之上实现连接生命周期管理,对于验证连接的有效性,采取的是在获取连接前验证的方法,该方法可能会导致额外的开销,可以采用空闲时检测,或者重入池回收时检测,另外还需测试长时间运行下是否能正确管理连接池,保证连接池正确运行。More important :如果发现有什么问题,或者有建议,希望大家能告诉我,谢谢。

0 0
原创粉丝点击