用Java实现简单的数据库连接池
来源:互联网 发布:淘宝网店打底衫长袖 编辑:程序博客网 时间:2024/05/24 06:59
1 引言
对于数据库连接池,相信大家都不会陌生,当下流行的连接池也有很多(如:DBCP,C3P0等),使用连接池可以在程序中给我们带来极大的方便,也可以有效的减少创建连接给我们带来的开销,前段时间公司的项目中有用到DBCP连接池,所以在闲暇之余,自己也尝试着写了一个简易数据库连接池,当然如果对
数据库连接池
不是很懂的话,建议参考Connection Pooling with Connector/J ,详细的介绍了连接池这项技术。
2 概述
下面介绍的这个简易连接池主要包含了一下内容和功能:
- 获取连接
- 回收连接
- 验证连接的有效性(获取连接前验证)
- 定期维护连接
- 当前连接耗尽后自动增加连接数
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
- 用Java实现简单的数据库连接池
- java实现简单的数据库连接池
- Java 的简单数据库连接池实现
- java数据库连接池简单实现
- 数据库连接池的简单实现
- 数据库连接池的简单实现
- 数据库连接池的简单实现
- 数据库连接池的简单实现
- 数据库连接池的简单实现
- 数据库连接池的简单实现
- DbConnection.java实现简单的MySQL数据库连接
- 数据库连接池的Java实现
- Java 数据库连接池的实现
- 一种简单JDBC数据库连接池的实现
- 一种简单JDBC数据库连接池的实现
- 一种简单JDBC数据库连接池的实现
- 一种简单JDBC数据库连接池的实现
- LinkedList实现简单的数据库连接池
- 实验二.任务二.标准体重计算器
- GUI编程
- hdu 1992-Tiling a Grid With Dominoes
- hdu 5889 Barricade 2016ACM/ICPC青岛赛区网络赛1011
- 464.Sort Integers II-整数排序 II(容易题)
- 用Java实现简单的数据库连接池
- 数据结构——单链表的操作
- leetcode Longest Palindromic Substring(C)
- BaseAdaPeter<笔记>
- 阿里云服务器磁盘空间不足解决办法
- 利用 ICMP 隧道穿透防火墙
- 469.Identical Binary Tree-等价二叉树(容易题)
- 堆栈
- 堆栈2