自己实现一个高并发可用的数据库连接池
简介
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
为什么要用连接池呢?
如果按照单个连接来进行数据库操作,在高并发的情况下会导致数据库连接数耗尽的问题,而且单个连接的频繁创建和关闭,极大地增加了数据库的开销。针对这些,数据库连接池技术就能很好的解决这些问题。
源码可在github上下载,欢迎评论交流
连接池主要是对连接对象的管理,那么我们就先来定义一个连接对象:
public class PoolConnection {
private Connection connect;
//false--繁忙,true--空闲
private boolean status;
public PoolConnection() {
}
public PoolConnection(Connection connect, boolean status) {
this.connect = connect;
this.status = status;
}
public Connection getConnect() {
return connect;
}
public void setConnect(Connection connect) {
this.connect = connect;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
//释放连接池中的连接对象
public void releaseConnect(){
System.out.println("-----------释放连接-----------");
this.status = true;
}
}
创建一个接口来获取这个连接对象:
public interface DataSource {
PoolConnection getDataSource();
}
连接数据库,需要一些基本参数配置,创建一个datasource.properties文件
jdbc.driver_class=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test?useSSL=false
jdbc.username=root
jdbc.password=root
initSize=2
stepSize=2
maxSize=6
timeout=2000
接下来就是根据配置文件的参数来创建连接池对象:
public class DataSourceImpl implements DataSource {
private ReentrantLock lock = new ReentrantLock();
//定义连接池中连接对象的存储容器
private List<PoolConnection> list = Collections.synchronizedList(new ArrayList<>());
//定义数据库连接属性
private final static String DRIVER_CLASS = PropertiesHolder.getInstance().getProperty("jdbc.driver_class");
private final static String URL = PropertiesHolder.getInstance().getProperty("jdbc.url");
private final static String USERNAME = PropertiesHolder.getInstance().getProperty("jdbc.username");
private final static String PASSWORD = PropertiesHolder.getInstance().getProperty("jdbc.password");
//定义默认连接池属性配置
private int initSize = 2;
private int maxSize = 4;
private int stepSize = 1;
private int timeout = 2000;
public DataSourceImpl() {
initPool();
}
//初始化连接池
private void initPool() {
String init = PropertiesHolder.getInstance().getProperty("initSize");
String step = PropertiesHolder.getInstance().getProperty("stepSize");
String max = PropertiesHolder.getInstance().getProperty("maxSize");
String time = PropertiesHolder.getInstance().getProperty("timeout");
initSize = init==null? initSize : Integer.parseInt(init);
maxSize = max==null? maxSize : Integer.parseInt(max);
stepSize = step==null? stepSize : Integer.parseInt(step);
timeout = time==null? timeout : Integer.parseInt(time);
try {
//加载驱动
Driver driver = (Driver) Class.forName(DRIVER_CLASS).newInstance();
//使用DriverManager注册驱动
DriverManager.registerDriver(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public PoolConnection getDataSource() {
PoolConnection poolConnection = null;
try{
lock.lock();
//连接池对象为空时,初始化连接对象
if(list.size() == 0){
createConnection(initSize);
}
//获取可用连接对象
poolConnection = getAvailableConnection();
//没有可用连接对象时,等待连接对象的释放或者创建新的连接对象使用
while(poolConnection == null){
System.out.println("---------------等待连接---------------");
createConnection(stepSize);
poolConnection = getAvailableConnection();
if(poolConnection == null){
TimeUnit.MILLISECONDS.sleep(30);
}
}
}catch(Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
return poolConnection;
}
//创建数据库连接
private void createConnection(int count) throws SQLException{
if(list.size() + count <= maxSize){
for(int i = 0; i < count; i++){
System.out.println("初始化了"+ (i + 1) +"个连接");
Connection connect = DriverManager.getConnection(URL, USERNAME, PASSWORD);
PoolConnection pool = new PoolConnection(connect, true);
list.add(pool);
}
}
}
//获取可用连接对象
private PoolConnection getAvailableConnection() throws SQLException{
for(PoolConnection pool : list){
if(pool.isStatus()){
Connection con = pool.getConnect();
//验证连接是否超时
if(!con.isValid(timeout)){
Connection connect = DriverManager.getConnection(URL, USERNAME, PASSWORD);
pool.setConnect(connect);
}
pool.setStatus(false);
return pool;
}
}
return null;
}
}
测试一下这个线程池是否可用:
public class App
{
public static void main( String[] args )
{
DataSource source = new DataSourceImpl();
CountDownLatch latch = new CountDownLatch(20);
for(int i = 0; i < 20; i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
PoolConnection connect = source.getDataSource();
TimeUnit.SECONDS.sleep(1);
connect.releaseConnect();
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
try {
latch.await();
System.out.println("-------结束-----------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果如下:
OK,自定义的数据库连接池就完成了。。。。