项目做不完可能最近不打算春招了?。一边复习,一边再学点新东西准备秋招吧!?好了伤心的话不多说了,连接池的基础概念简单地搬一下。
在内部对象池中,维护一定数量的数据库连接,并对外暴露数据库连接的获取和返回方法。
如外部使用者可通过getConnection方法获取数据库连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时的连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。
C3P0
C3P0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
DBCP
DBCP (Database Connection Pool)是一个依赖Jakarta commons-pool对象池机制的数据库连接本对应的是 JDK 1.4-1.5 和 JDBC 3,而1.4 版本对应 JDK 1.6 和 JDBC 4。因此在选择版本的时候要看看你用的是什么 JDK 版本了,功能上倒是没有什么区别。
dbcp没有自动的去回收空闲连接的功能 c3p0有自动回收空闲连接功能 两者主要是对数据连接的处理方式不同!C3P0提供最大空闲时间,DBCP提供最大连接数。 前者当连接超过最大空闲连接时间时,当前连接就会被断掉。DBCP当连接数超过最大连接数时,所有连接都会被断。
定义两个线程安全的容器分别来装,空闲连接和活动连接。在初始化一个带参数的(参数大小为最大连接数)LinkedBlockingQueue。
这里用了一个原子类来计数(count),保证多线程下的安全性。负责记录当前连接池中的连接数。
init()方法:负责初始化空闲连接数。
newConnection()方法:负责创建一个新连接。
getConnection()方法:负责建立一个活动连接。(核心)
releaseConnection(Connection connection)释放连接方法:如果连接时可用的同时空闲链接还能存放,就放入到空闲连接里面,否则就直接关闭。在这里需要强调:“记得消费队列里面的对象,与阻塞等待形成一个通讯。”
完整代码附上✍:
package com.xianglei.bean;
/**
* @author 作者xianglei:
* @version 创建时间:2019年4月8日 下午6:04:02
* com.xianglei.bean
*
*/
//外部配置文件信息
public class DbBean {
/* 链接属性 */
private String driverName = "com.mysql.jdbc.Driver";
private String url = "jdbc:mysql://localhost:3306/test";
private String userName = "root";
private String password = "root";
private String poolName = "SuperPool";// 连接池名字
private int minConnections = 1; // 空闲池,最小连接数
private int maxConnections = 10; // 空闲池,最大连接数
private int initConnections = 5;// 初始化连接数
private long connTimeOut = 1000;// 重复获得连接的频率
private int maxActiveConnections = 30;// 最大允许的连接数,和数据库对应
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;
}
}
// 这个接口是自己定义的,规定了一个get和release方法
package com.xianglei.conn;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.xianglei.bean.DbBean;
import com.xianglei.inter.MyConnectionPool;
/**
*
* @author 作者xianglei:
*
* @version 创建时间:2019年4月8日 下午 6:02:39
* com.xianglei.conn 手写数据库连接池 用阻塞队列实现一个阻塞效果
*
*/
public class ConnectionPool implements MyConnectionPool {
// 设置一个阻塞队列来实现当新来的连接大于最大连接等待的情况
LinkedBlockingQueue<Object> queue = null;
// 空闲连接容器
List<Connection> freeconn = new Vector<Connection>();
// 活动线程容器
List<Connection> activeconn = new Vector<Connection>();
// 记录当前线程数 保证线程安全
AtomicInteger count = null;
DbBean dbBean;
public ConnectionPool(DbBean dbBean) {
this.dbBean = dbBean;
init();
}
// 初始化一个空闲连接
public void init() {
count=new AtomicInteger(0);
queue = new LinkedBlockingQueue<>(dbBean.getMaxActiveConnections());
try {
for (int i = 0; i < dbBean.getInitConnections(); i++) {
Connection newConnection = newConnection();
if (newConnection != null) {
// 添加到空闲线程中...
freeconn.add(newConnection);
}
}
// 设置阻塞队列的大小
} catch (Exception e) {
}
}
private Connection newConnection() {
try {
if (dbBean != null) {
Class.forName(dbBean.getDriverName());
try {
Connection connection = DriverManager.getConnection(dbBean.getUrl(), dbBean.getUserName(),
dbBean.getPassword());
// 连接数+1
count.incrementAndGet();
// 新创建一个连接 队列就+1;
queue.offer(new Object());
return connection;
} catch (SQLException e) {
e.printStackTrace();
}
} else {
return null;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 核心 如何获取(建立)一个活动连接
*/
@Override
public synchronized Connection getConnection() {
Connection newConnection = null;
// 1.判断活动线程数是否大于最大线程 如果大于最大线程数,则进行阻塞等待...
if (count.get() < dbBean.getMaxActiveConnections()) {
// 如果还有空闲连接
if (freeconn.size() > 0) {
newConnection = freeconn.remove(0);
} else {
// 创建新的连接
newConnection = newConnection();
// 判断一下是否成功创建
boolean available = isAvilible(newConnection);
if (available) {
activeconn.add(newConnection);
} else {
count.decrementAndGet(); // i--操作 因为每次new的时候就+1了 当new
queue.poll(); // 出来的不可用就减去
newConnection = getConnection(); // 递归调用getConnection方法
}
}
// 大于最大连接数只能等待 可以设置一个重复请求的时间
} else {
/*************************************************************/
try {
// 每当有一个新的连接建立 队列就 +1
// 可以设定等待的时间,如果在指定的时间内,还不能往队列中加入BlockingQueue,则返回失败
queue.offer(new Object(), dbBean.getConnectionTimeOut(), TimeUnit.SECONDS);
newConnection = getConnection();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return newConnection;
}
// 返回连接
@Override
public synchronized void releaseConnection(Connection connection) {
try {
if (connection != null) {
// 判断一下防止回收一个没用的连接
if (isAvilible(connection)) {
// 判断一下空闲连接是否大于最大连接
if (freeconn.size() < dbBean.getMaxConnections()) {
freeconn.add(connection);
} else {
// 空闲线程数已经满了
connection.close();
}
// -1
activeconn.remove(connection);
count.decrementAndGet();
// 队列消耗-1
queue.poll();
}
}
} catch (Exception e) {
}
}
// 抽出一个判断线程是否可用
public boolean isAvilible(Connection connection) {
try {
if (connection != null || !connection.isClosed()) {
return true;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
}
模拟测试:
public class DBThread implements Runnable {
ConnectionPool connection = null;
Connection connection2 =null;
public DBThread(ConnectionPool connection) {
this.connection = connection;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 15; i++) {
connection2= connection.getConnection();
System.out.println(Thread.currentThread().getName() + ",connection:---" +connection2 +"获取连接成功!");
/*try {
System.out.println("------------------------即将释放一个连接--------------------");
Thread.sleep(1000);
connection.releaseConnection(connection2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
}
}
}
/**
*
* @author 作者xianglei:
*
* @version 创建时间:2019年4月8日 下午5:55:55 com.xianglei.bean
*
*
*/
public class Test {
public static void main(String[] args) {
DbBean dbBean = new DbBean();
// 使用同一个连接池
ConnectionPool connectionPool = new ConnectionPool(dbBean);
DBThread dBThread = new DBThread(connectionPool);
DBThread dBThread2 = new DBThread(connectionPool);
DBThread dBThread3 = new DBThread(connectionPool);
// 模拟三个用户每个用户开启了10个连接
Thread thread = new Thread(dBThread, "用户线程--1" );
Thread thread2 = new Thread(dBThread2, "用户线程--2" );
Thread thread3 = new Thread(dBThread3, "用户线程--3");
thread.start();
thread2.start();
thread3.start();
}
}
? 如果你发现了小bug你尽管提出来,毕竟只是实现了一个简单的连接池,总体来说原理还是比较简单的。当然在这里面除了用阻塞队列你也可用wait notifyall等等。