功能集:獲取連接,回收連接,檢查連接狀態以及定時自檢所有空閒連接等
一、連接池配置屬性
參數 | 說明 |
---|---|
driver | 連接驅動 |
url | 連接地址 |
username | 連接名 |
password | 連接密碼 |
minFreeConnections | 空閒連接池,最小連接數,默認爲2 |
maxFreeConnections | 空閒連接池,最大連接數,默認爲8 |
maxActiveConnection | 活躍連接池,最大連接數,默認爲8 |
initConnections | 初始化連接數,默認爲2個 |
connectionTimeOut | 連接超時時間,默認爲20分鐘 |
recheckTime | 自檢循環時間,默認爲60秒 |
類代碼編寫:DbConfig.java
package com.nys.dbpool;
/**
* 配置連接池屬性信息
* @author nysheng
*
*/
public class DbConfig {
private String driver; //連接驅動
private String url; //連接地址
private String username; //連接名
private String password; //連接密碼
private Integer minFreeConnections=2; //空閒連接池,最小連接數,默認爲2
private Integer maxFreeConnections=8; //空閒連接池,最大連接數,默認爲8
private Integer maxActiveConnection=8; //活躍連接池,最大連接數,默認爲8
private Integer initConnections=2; //初始化連接數,默認爲2個
private Long connectionTimeOut=1000*60*20L; //連接超時時間,默認爲20分鐘
private Long recheckTime=1000*60L; //自檢循環時間,默認爲60秒
// .......get/set方法.......
}
二、連接池
參數 | 說明 |
---|---|
freePool | 空閒連接池 |
activePool | 活躍連接池 |
dbConfig | 連接池配置 |
countConnection | 記錄已創建的連接數 |
類代碼編寫:ConnectionPool.java
package com.nys.dbpool;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import com.nys.enums.ResultEnum;
import com.nys.exceptions.ConnectionPoolException;
import java.sql.Connection;
/**
* 連接池
* @author nysheng
*
*/
public class ConnectionPool {
//空閒連接池
private CopyOnWriteArrayList<Connection> freePool=new CopyOnWriteArrayList<Connection>();
//活躍連接池
private CopyOnWriteArrayList<Connection> activePool=new CopyOnWriteArrayList<Connection>();
//連接池配置
private DbConfig dbConfig;
//記錄已創建的連接數
private AtomicInteger countConnection=new AtomicInteger();
public ConnectionPool(DbConfig dbConfig) {
this.dbConfig=dbConfig;
try {
Class.forName(dbConfig.getDriver());
//1. 初始化連接池
for(int i=0;i<dbConfig.getInitConnections();i++) {
Connection connection=newConnection();
if(connection!=null) {
freePool.add(connection);
}
}
//2.新開一線程,啓動自檢機制
new Thread(new Runnable() {
@Override
public void run() {
recheckConnection();
}
}).start();
} catch (ClassNotFoundException e) {
throw new ConnectionPoolException(ResultEnum.DRIVER_NOT_FOUND);
}
}
/**
* 創建新連接
* @return
*/
private synchronized Connection newConnection() {
Connection connection=null;
try {
connection=DriverManager.getConnection(dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword());
countConnection.incrementAndGet();
} catch (SQLException e) {
throw new ConnectionPoolException(ResultEnum.CONNECTION_FAIL);
}
return connection;
}
/**
* 判斷連接是否可用
* @param connection
* @return
*/
private boolean isAlive(Connection connection) {
try {
if(connection==null||connection.isClosed()) {
return false;
}
} catch (SQLException e) {
throw new ConnectionPoolException(ResultEnum.CONNECTION_STATUS_ERROE);
}
return true;
}
/**
* 檢查當前連接數是否低於最低線程數,是則創建新線程
*/
private synchronized void recheckConnection() {
//1.檢查所有空閒連接是否可用,不可用的直接關閉連接
//使用迭代器來進行數據的遍歷刪除,避免快速迭代失敗
Iterator<Connection> it=freePool.iterator();
while(it.hasNext()) {
if(!isAlive(it.next())) {
it.remove();
}
}
//2.檢查當前連接數是否滿足最低空閒連接數,若低於最小空閒數,新增連接放入空閒池
final int count=countConnection.get();
if(count<dbConfig.getMinFreeConnections()) {
for(int i=count;i<2;i++) {
Connection connection=newConnection();
if(connection!=null) {
freePool.add(connection);
}
}
}
try {
long start=System.currentTimeMillis();
while(System.currentTimeMillis()-start<dbConfig.getRecheckTime()) {
wait(dbConfig.getRecheckTime());
}
recheckConnection();
} catch (InterruptedException e) {
throw new ConnectionPoolException(ResultEnum.INTERRUPT_ERROR);
}
}
/**
* 從連接池中獲取連接
* @return
*/
public synchronized Connection getConnecion() {
Connection connection=null;
//1. 判斷當前已連接數是否小於最大活躍連接數
if(activePool.size()<dbConfig.getMaxActiveConnection()) {
//2.如果空閒連接池裏存在連接,拿出連接
if(freePool.size()>0) {
connection=freePool.remove(0);
}else {
//3.空閒連接不夠,創建新連接
connection=newConnection();
}
if(isAlive(connection)) {
activePool.add(connection);
}
}else {
//4.活躍連接池內連接數已滿,阻塞線程,等待喚醒
try {
wait(dbConfig.getConnectionTimeOut());
} catch (InterruptedException e) {
throw new ConnectionPoolException(ResultEnum.INTERRUPT_ERROR);
}
return getConnecion();
}
return connection;
}
public synchronized boolean releaseConnection(Connection connection) {
//1.判斷連接是否可用
if(isAlive(connection)) {
//2.判斷空閒池是否已滿
if(freePool.size()<dbConfig.getMaxFreeConnections()) {
//未滿,回收連接
freePool.add(connection);
}else {
//已滿,關閉連接並減少連接計數
try {
countConnection.decrementAndGet();
connection.close();
} catch (SQLException e) {
throw new ConnectionPoolException(ResultEnum.CONNECTION_CLOSE_FAIL);
}
}
//3.從活躍池中移除連接
activePool.remove(connection);
//4.喚醒所有被阻塞的線程
notifyAll();
return true;
}
return false;
}
}
三、調用方式(單例+配置文件)
配置文件:jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/nys_step5?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=123456
# 空閒連接池,最小連接數,默認爲2
jdbc.minFreeConnections=2
# 空閒連接池,最大連接數,默認爲8
jdbc.maxFreeConnections=8
# 活躍連接池,最大連接數,默認爲8
jdbc.maxActiveConnection=8
# 初始化連接數,默認爲2
jdbc.initConnections=2
# jdbc.connectionTimeOut=1000*60*20L; //連接超時時間,默認爲20分鐘,不支持自定義
# jdbc.recheckTime=1000*60L; //自檢循環時間,默認爲60秒,不支持自定義
單例類:DbPoolSingleton.java
package com.nys.singleton;
import com.nys.dbpool.ConnectionPool;
import com.nys.dbpool.DbConfig;
import com.nys.exceptions.MyIOException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* 數據庫連接池單例對象-靜態內部類
* @author nysheng
*/
public class DbPoolSingleton {
private static class Builder{
//1.加載jdbc配置文件,配置連接池信息
private static DbConfig dbConfig=null;
static{
dbConfig=new DbConfig();
Properties properties=new Properties();
InputStream inputStream=DbPoolSingleton.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
properties.load(inputStream);//加載properties文件
String url=properties.getProperty("jdbc.url");
String driver=properties.getProperty("jdbc.driver");
String username=properties.getProperty("jdbc.username");
String password=properties.getProperty("jdbc.password");
String minFreeConnections=properties.getProperty("jdbc.minFreeConnections");
String maxFreeConnections=properties.getProperty("jdbc.maxFreeConnections");
String maxActiveConnection=properties.getProperty("jdbc.maxActiveConnection");
String initConnections=properties.getProperty("jdbc.initConnections");
dbConfig.setUrl(url);
dbConfig.setDriver(driver);
dbConfig.setUsername(username);
dbConfig.setPassword(password);
//空閒連接池,最小連接數,默認爲2
dbConfig.setMinFreeConnections(minFreeConnections==null||minFreeConnections.equals("")?2:Integer.parseInt(minFreeConnections));
//空閒連接池,最大連接數,默認爲8
dbConfig.setMaxFreeConnections(maxFreeConnections==null||maxFreeConnections.equals("")?8:Integer.parseInt(maxFreeConnections));
//活躍連接池,最大連接數,默認爲8
dbConfig.setMaxActiveConnection(maxActiveConnection==null||maxActiveConnection.equals("")?8:Integer.parseInt(maxActiveConnection));
//初始化連接數,默認爲2個
dbConfig.setInitConnections(initConnections==null||initConnections.equals("")?2:Integer.parseInt(initConnections));
} catch (IOException e) {
throw new MyIOException(e.getMessage());
}
}
//2.創建連接池對象
public static ConnectionPool connectionPool=new ConnectionPool(dbConfig);
}
public static ConnectionPool newInstance(){
return Builder.connectionPool;
}
}
四、全局異常處理
使用枚舉定義異常狀態:ResultEnum
package com.nys.enums;
/**
* 異常消息
* @author nysheng
*
*/
public enum ResultEnum {
DRIVER_NOT_FOUND(10000,"加載驅動失敗"),
CONNECTION_FAIL(10001,"建立連接失敗"),
CONNECTION_STATUS_ERROE(10002,"連接狀態異常"),
CONNECTION_CLOSE_FAIL(10003,"連接關閉失敗"),
INTERRUPT_ERROR(10004,"阻塞異常");
private Integer code;
private String msg;
ResultEnum(Integer code,String msg){
this.code=code;
this.msg=msg;
}
ResultEnum(){}
public String getMsg() {
return msg;
}
public Integer getCode() {
return code;
}
}
自定義異常:ConnectionPoolException.java
package com.nys.exceptions;
import com.nys.enums.ResultEnum;
/**
* 連接池異常處理
* @author nysheng
*
*/
public class ConnectionPoolException extends RuntimeException {
private static final long serialVersionUID = 1L;
private Integer code;
public ConnectionPoolException() {
super();
code=0;
}
public ConnectionPoolException(ResultEnum resultEnum) {
super(resultEnum.getMsg());
this.code=resultEnum.getCode();
}
public Integer getCode() {
return code;
}
}