实现数据库连接池的基本功能,包括但不限于获取数据库连接、维持数据库连接、释放数据库连接。
自定义ConnectionPool
类,完成数据库连接建立、维持、释放的功能。大致步骤如下:
initConnections
个数量的数据库连接。freeConnection
空闲连接的数量是否大于0
,如果大于,则直接返回连接。如果小于则判断当前连接数量是否小于maxActiveConnections
最大允许的连接数,如果不小于,则自我阻塞并等待,如果小于则新建一个新的连接返回给用户。maxConnections
空闲池最大允许保有量,如果不大于,则直接放入到空闲连接池中。同时将活动连接池中连接删除。如果大于则需要释放该连接,并通知阻塞的连接获取连接。实现数据库连接建立、维持、释放的功能。
本项目是自定义连接池,需要对数据库访问,所以需要添加MySQL
驱动包。源码如下:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>handwritingprojectartifactId>
<groupId>com.njustgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>databaseConnectionartifactId>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.38version>
dependency>
dependencies>
project>
定义DbBean
类,主要存储数据库连接池的基本信息、供用户个性化配置。如数据库用户名、密码、连接池名称、空闲池最大连接数等基本信息。源码如下:
DbBean.java
package com.njust.connection;
//外部配置文件信息
public class DbBean {
/* 链接属性 */
private String driverName = "com.mysql.jdbc.Driver";
private String url = "jdbc:mysql://localhost:3306/mysql_learning?useUnicode=true&characterEncoding=UTF8";
private String userName = "root";
private String password = "root";
private String poolName = "ChenConnectionPool";// 连接池名字
private int minConnections = 1; // 空闲池,最小连接数
private int maxConnections = 10; // 空闲池,最大连接数
private int initConnections = 5;// 初始化连接数
private long connTimeOut = 1000;// 重复获得连接的频率
private int maxActiveConnections = 100;// 最大允许的连接数,和数据库对应
private long connectionTimeOut = 1000 * 60 * 20;// 连接超时时间,默认20分钟
public String getDriverName() {
return driverName;
}
public void setDriverName(String driverName) {
this.driverName = driverName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPoolName() {
return poolName;
}
public void setPoolName(String poolName) {
this.poolName = poolName;
}
public int getMinConnections() {
return minConnections;
}
public void setMinConnections(int minConnections) {
this.minConnections = minConnections;
}
public int getMaxConnections() {
return maxConnections;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public int getInitConnections() {
return initConnections;
}
public void setInitConnections(int initConnections) {
this.initConnections = initConnections;
}
public long getConnTimeOut() {
return connTimeOut;
}
public void setConnTimeOut(long connTimeOut) {
this.connTimeOut = connTimeOut;
}
public int getMaxActiveConnections() {
return maxActiveConnections;
}
public void setMaxActiveConnections(int maxActiveConnections) {
this.maxActiveConnections = maxActiveConnections;
}
public long getConnectionTimeOut() {
return connectionTimeOut;
}
public void setConnectionTimeOut(long connectionTimeOut) {
this.connectionTimeOut = connectionTimeOut;
}
}
定义IConnectionPool
类,对外提供获取连接getConnection()
,和释放连接releaseConnection()
的接口。源码如下:
IConnectionPool.java
package com.njust.connection;
import java.sql.Connection;
//连接数据库池
public interface IConnectionPool {
// 获取连接(重复利用机制)
public Connection getConnection();
// 释放连接(可回收机制)
public void releaseConnection(Connection connection);
}
新建ConnectionPool
类,该类主要完成数据库连接建立、维持、释放的功能。大致步骤如下:首先初始化时,新建数据库连接,同时在容器中存储用户定义的initConnections
个数量的数据库连接。用户获取连接时,首先判断freeConnection
空闲连接的数量是否大于0
,如果大于,则直接返回连接。如果小于则判断当前连接数量是否小于maxActiveConnections
最大允许的连接数,如果不小于,则自我阻塞并等待,如果小于则新建一个新的连接返回给用户。用户释放连接时,首先判断空闲连接池中连接数量是否大于maxConnections
空闲池最大允许保有量,如果不大于,则直接放入到空闲连接池中。同时将活动连接池中连接删除。如果大于则需要释放该连接,并通知阻塞的连接获取连接。源码如下:
ChenTransaction.java
package com.njust.connection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;
public class ConnectionPool implements IConnectionPool {
// 使用线程安全的集合 空闲线程 容器 没有被使用的连接存放
private List<Connection> freeConnection = new Vector<Connection>();
// 使用线程安全的集合 活动线程 容器 容器正在使用的连接
private List<Connection> activeConnection = new Vector<Connection>();
private DbBean dbBean;
private AtomicInteger countConne = new AtomicInteger(0);
public ConnectionPool(DbBean dbBean) {
// 获取配置文件信息
this.dbBean = dbBean;
init();
System.out.println();
}
// 初始化线程池(初始化空闲线程)
private void init() {
if (dbBean == null) {
return;// 注意最好抛出异常
}
// 1.获取初始化连接数
for (int i = 0; i < dbBean.getInitConnections(); i++) {
// 2.创建Connection连接
Connection newConnection = newConnection();
if (newConnection != null) {
// 3.存放在freeConnection集合
freeConnection.add(newConnection);
}
}
}
// 创建Connection连接
private Connection newConnection() {
try {
// 装载一个类并且对其进行实例化的操作。
Class.forName(dbBean.getDriverName());
Connection connection = DriverManager.getConnection(dbBean.getUrl(), dbBean.getUserName(),
dbBean.getPassword());
countConne.incrementAndGet();
return connection;
} catch (Exception e) {
return null;
}
}
// 调用getConnection方法 --- 获取连接
public Connection getConnection() {
try {
Connection connection = null;
// 思考:怎么知道当前创建的连接>最大连接数
if (countConne.get() < dbBean.getMaxActiveConnections()) {
// 小于最大活动连接数
// 1.判断空闲线程是否有数据
if (freeConnection.size() > 0) {
// 空闲线程有存在连接
// ==freeConnection.get(0);freeConnection.remove(0)
// 拿到在删除
connection = freeConnection.remove(0);
} else {
// 创建新的连接
connection = newConnection();
}
// 判断连接是否可用
boolean available = isAvailable(connection);
if (available) {
// 存放在活动线程池
activeConnection.add(connection);
} else {
connection = getConnection();// 怎么使用重试? 递归算法
}
} else {
// 大于最大活动连接数,进行等待
wait(dbBean.getConnTimeOut());
// 重试
connection = getConnection();
}
return connection;
} catch (Exception e) {
return null;
}
}
// 判断连接是否可用
public boolean isAvailable(Connection connection) {
try {
if (connection == null || connection.isClosed()) {
return false;
}
} catch (Exception e) {
}
return true;
}
// 释放连接 回收
public synchronized void releaseConnection(Connection connection) {
try {
// 1.判断连接是否可用
if (isAvailable(connection)) {
// 2.判断空闲线程是否已满
if (freeConnection.size() < dbBean.getMaxConnections()) {
// 空闲线程没有满
freeConnection.add(connection);// 回收连接
} else {
// 空闲线程已经满
connection.close();
countConne.decrementAndGet();
}
activeConnection.remove(connection);
notifyAll();
}
} catch (Exception e) {
// TODO: handle exception
}
}
public int getCountConne() {
return countConne.get();
}
}
新建ConnectionPoolManager
类,单例实现线程池的管理,防止过多实例创建导致内存溢出。源码如下:
ConnectionPoolManager .java
package com.njust.connection;
import java.sql.Connection;
// 管理线程池
public class ConnectionPoolManager {
private static DbBean dbBean = new DbBean();
private static ConnectionPool connectionPool = new ConnectionPool(dbBean);
// 获取连接(重复利用机制)
public static Connection getConnection() {
return connectionPool.getConnection();
}
// 释放连接(可回收机制)
public static void releaseConnection(Connection connection) {
connectionPool.releaseConnection(connection);
}
public static int getCount() {
return connectionPool.getCountConne();
}
}
新建Test001
类,其中ThreadConnection
不停的获取和释放连接,主线程新建30
个这样的线程,压力测试数据库连接池的性能。源码如下:
Test001 .java
package com.njust.connection;
import java.sql.Connection;
public class Test001 {
public static void main(String[] args) {
ThreadConnection threadConnection = new ThreadConnection();
for (int i = 1; i < 30; i++) {
Thread thread = new Thread(threadConnection, "线程i:" + i);
thread.start();
}
}
}
class ThreadConnection implements Runnable {
public void run() {
for (int i = 0; i < 100; i++) {
Connection connection = ConnectionPoolManager.getConnection();
System.out.println(Thread.currentThread().getName() + ",connection:" + connection);
ConnectionPoolManager.releaseConnection(connection);
}
System.out.println(Thread.currentThread().getName() + " : " + ConnectionPoolManager.getCount());
}
}
由于输出信息过多,我暂时截取部分输出。我们可以发现connection:com.mysql.jdbc.JDBC4Connection@16614a20
连接被复用了多次。有效的减少了数据库连接释放的资源消耗。控制台输出如下:
Console
线程i:2,connection:com.mysql.jdbc.JDBC4Connection@416d0bfc
线程i:2,connection:com.mysql.jdbc.JDBC4Connection@4d59c246
线程i:2,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:2,connection:com.mysql.jdbc.JDBC4Connection@416d0bfc
线程i:26,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:7,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:7,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:7,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:7,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:7,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:7,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:7,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:7,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:7,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:7,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:7 : 5
...
线程i:24,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:24,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:24,connection:com.mysql.jdbc.JDBC4Connection@7b0dfcb9
线程i:24,connection:com.mysql.jdbc.JDBC4Connection@416d0bfc
线程i:24,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:6,connection:com.mysql.jdbc.JDBC4Connection@42903f23
线程i:6,connection:com.mysql.jdbc.JDBC4Connection@7b0dfcb9
线程i:6,connection:com.mysql.jdbc.JDBC4Connection@416d0bfc
线程i:6,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:6,connection:com.mysql.jdbc.JDBC4Connection@42903f23
线程i:6,connection:com.mysql.jdbc.JDBC4Connection@7b0dfcb9
线程i:6,connection:com.mysql.jdbc.JDBC4Connection@416d0bfc
线程i:6,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:6,connection:com.mysql.jdbc.JDBC4Connection@42903f23
线程i:4,connection:com.mysql.jdbc.JDBC4Connection@50230424
线程i:4,connection:com.mysql.jdbc.JDBC4Connection@416d0bfc
线程i:4,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:4,connection:com.mysql.jdbc.JDBC4Connection@42903f23
线程i:14,connection:com.mysql.jdbc.JDBC4Connection@14adf04d
线程i:1,connection:com.mysql.jdbc.JDBC4Connection@6cfc31a0
线程i:14,connection:com.mysql.jdbc.JDBC4Connection@416d0bfc
线程i:5,connection:com.mysql.jdbc.JDBC4Connection@4d59c246
线程i:14,connection:com.mysql.jdbc.JDBC4Connection@42903f23
线程i:1,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:4,connection:com.mysql.jdbc.JDBC4Connection@50230424
线程i:6,connection:com.mysql.jdbc.JDBC4Connection@7b0dfcb9
线程i:24,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:6 : 9
线程i:4,connection:com.mysql.jdbc.JDBC4Connection@4d59c246
线程i:4,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:4,connection:com.mysql.jdbc.JDBC4Connection@50230424
线程i:1,connection:com.mysql.jdbc.JDBC4Connection@416d0bfc
线程i:14,connection:com.mysql.jdbc.JDBC4Connection@6cfc31a0
线程i:5,connection:com.mysql.jdbc.JDBC4Connection@14adf04d
线程i:14,connection:com.mysql.jdbc.JDBC4Connection@4d59c246
线程i:1,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:4,connection:com.mysql.jdbc.JDBC4Connection@7b0dfcb9
线程i:24,connection:com.mysql.jdbc.JDBC4Connection@42903f23
线程i:4,connection:com.mysql.jdbc.JDBC4Connection@6cfc31a0
线程i:1,connection:com.mysql.jdbc.JDBC4Connection@416d0bfc
线程i:14,connection:com.mysql.jdbc.JDBC4Connection@50230424
线程i:5,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:14,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:1,connection:com.mysql.jdbc.JDBC4Connection@4d59c246
线程i:4 : 9
线程i:24,connection:com.mysql.jdbc.JDBC4Connection@14adf04d
线程i:24 : 9
线程i:1,connection:com.mysql.jdbc.JDBC4Connection@6cfc31a0
线程i:14,connection:com.mysql.jdbc.JDBC4Connection@42903f23
线程i:5,connection:com.mysql.jdbc.JDBC4Connection@7b0dfcb9
线程i:14,connection:com.mysql.jdbc.JDBC4Connection@50230424
线程i:14,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:14 : 9
线程i:1,connection:com.mysql.jdbc.JDBC4Connection@416d0bfc
线程i:1,connection:com.mysql.jdbc.JDBC4Connection@4d59c246
线程i:1,connection:com.mysql.jdbc.JDBC4Connection@14adf04d
线程i:1 : 9
线程i:5,connection:com.mysql.jdbc.JDBC4Connection@16614a20
线程i:5,connection:com.mysql.jdbc.JDBC4Connection@6cfc31a0
线程i:5,connection:com.mysql.jdbc.JDBC4Connection@42903f23
线程i:5,connection:com.mysql.jdbc.JDBC4Connection@7b0dfcb9
线程i:5,connection:com.mysql.jdbc.JDBC4Connection@50230424
线程i:5,connection:com.mysql.jdbc.JDBC4Connection@14c448fe
线程i:5 : 9
Process finished with exit code 0
获取连接
释放连接
public synchronized void releaseConnection(Connection connection) {
try {
// 1.判断连接是否可用
if (isAvailable(connection)) {
// 2.判断空闲线程是否已满
if (freeConnection.size() < dbBean.getMaxConnections()) {
// 空闲线程没有满
freeConnection.add(connection);// 回收连接
} else {
// 空闲线程已经满
connection.close();
countConne.decrementAndGet();
}
activeConnection.remove(connection);
notifyAll();
}
} catch (Exception e) {
// TODO: handle exception
}
}
在释放资源的时候一定要调用notifyAll();
方法通知阻塞线程获取连接,否则阻塞线程将一直等待。释放方法一定要加synchronized
属性,否则会出现死锁问题。
private AtomicInteger countConne = new AtomicInteger(0);
一定要使用线程安全的原子变量记录当前连接数量,否则会出现并发安全问题。
有问题欢迎各位读者批评指正。
点个赞再走呗!欢迎留言哦!