以前一直在做android,用的是sqlite,最近做的东西需要用到数据库,第一次接触数据库,用jdbc。本来用一个单例一条连接,好像connection是线程安全的,测试了多线程环境下,有一些连接等待了超过一分钟时间,所以就写了一个数据库连接池,其实是跟数据库无关的东西。测试了一下,用2500条线程,会极少数连接耗时会达到3000毫秒,大多数是在100以内,贴上代码,大家看看。东西有借有还,用了SqlConnection一定要还,不然不知道会出什么问题。代码有注释,出bug了大家交流交流。
1、数据库连接池操作类,主要用于获取数据库连接,并且当数据库连接用完了以后的归还方法。同时在这里实例化闲置超时监听。
package com.allin.servers.sql.pool;
/**
* 数据库连接池操作类
*
* @author CSC
*
*/
public class SqlPoolManager {
private static SqlPoolManager me;
private static Object lock = new Object();
private SqlPool pool;
/**
* 构造函数,在实例化的同时实例化连接池类。
*/
private SqlPoolManager() {
pool = new SqlPool();
pool.setListener(new Listener());
pool.init();
}
/**
* 采用恶汉式单例,唯一的获取连接池操作类实例的方法。
*
* 加同步避免多线程调用多次实例化该类。
*
* @return
*/
public static SqlPoolManager get() {
if (me == null) {
synchronized (lock) {
if (me == null) {
me = new SqlPoolManager();
}
}
}
return me;
}
/**
* 获得数据库连接类。
*
* @return
*/
public SqlConnection getConnection() {
SqlConnection conn = pool.getConnection();
return conn;
}
/**
* 在用完数据库连接类后将连接类归还方法。
*
* @param connection
*/
public void returnConnection(SqlConnection connection) {
pool.returnConnection(connection);
}
/**
* 私有内部类,不允许其他调用类实例化该类
*
* @author uuu
*
*/
private class Listener implements SqlConnectTimeoutListener {
public void connectTimeOut(SqlConnection conn) {
pool.removeConnection(conn);
}
}
}
2、数据库连接池,主要维持了一个用于保持数据库连接的集合,并且控制连接池的连接数量,处理连接类的分发归还规则。
package com.allin.servers.sql.pool;
import java.util.ArrayList;
/**
* 1、数据库连接池,设立数据库连接缓冲区,优化连接性能。
* 2、默认最大连接数为100.当连接数未达到100时,每增加一个连接则增加一个connection,默认最小连接10.
* 3、当达到最大连接请求超过100则为每一条connection分配连接请求。 4、不设超时。
*
* @author CSC
*
*/
public class SqlPool {
Object lock = new Object();
SqlConnectTimeoutListener listener;
/**
* 记录当前共建立了多少连接。
*/
int count;
/**
* 处于计时状态的数量
*/
int timingCount;
/**
* 连接池初始容量,该容量影响在数据量少的时候的数据库连接速度,如果设置过大则是系统维护太多连接,并且导致启动慢。
*/
private int initialCapacity;
/**
* 连接池最大容量。
*/
private int maxCapacity;
/**
* 线程安全的数据库连接维持集合。
*/
ArrayList<SqlConnection> connections = new ArrayList<SqlConnection>();
/**
* 创建一个默认初始容量为10,最大容量为100的连接池。
*/
public SqlPool() {
this(100);
}
/**
* 创建一个默认初始容量为10,最大容量为maxCapacity的连接池。
*
* @param maxCapacity
* 最大容量。
*/
public SqlPool(int maxCapacity) {
this(10, maxCapacity);
}
/**
* 创建一个初始容量为initialCapacity,最大容量为maxCapacity的连接池。
*
* @param initialCapacity
* 初始容量。
* @param maxCapacity
* 最大容量。
*/
public SqlPool(int initialCapacity, int maxCapacity) {
this.initialCapacity = initialCapacity;
this.maxCapacity = maxCapacity;
}
/**
* 获得数据库连接.
*
* @return
*/
public synchronized SqlConnection getConnection() {
if (!connections.isEmpty()) {
SqlConnection conn = getOutConn();
System.out.println("!isEmpty .. count is : " + count
+ " size is : " + connections.size());
return conn;
}
if (count < maxCapacity) {
SqlConnection conn = getNewConn();
System.out.println("new connetion");
return conn;
}
try {
while (connections.isEmpty()) {
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return getOutConn();
}
/**
* 取消计时,并将正在计时的数量 -1
*
* @param conn
*/
private void cancelTimer(SqlConnection conn) {
boolean isCancel = conn.cancelTimer();
if (isCancel)
timingCount--;
}
/**
* 开始计时,并将正在计时的数量 +1
*
* @param conn
*/
private void timing(SqlConnection conn) {
conn.timing();
System.out.println("timing");
timingCount++;
}
/**
* 移除数据库连接.
*
* @param connection
*/
public synchronized void removeConnection(SqlConnection connection) {
for (int i = 0; i < connections.size(); i++) {
if (connection == connections.get(i)) {
connection.close();
connections.remove(i);
count--;
timingCount--;
System.out.println("count is : " + count + ". i is : " + i
+ ". timingCount is : " + timingCount);
break;
}
}
}
/**
* 获得即将分出去的数据库连接,将最早加入的分出去。
*
* @return
*/
private SqlConnection getOutConn() {
SqlConnection conn = connections.remove(0);
cancelTimer(conn);
return conn;
}
/**
* 归还已经分出去的连接,同时将归还的连接启动闲置计时器。
*
* 当总创建连接数与正在计时数的差值大于最小连接数开始计时,以保证一共有initialCapacity条连接不被释放。
*
* @param conn
*/
public synchronized void returnConnection(SqlConnection conn) {
connections.add(conn);
if ((count - timingCount) > initialCapacity) {
timing(conn);
}
notifyAll();
}
/**
* 新建sql连接,在新建的同时将连接数增加1
*
* @return
*/
private SqlConnection getNewConn() {
SqlConnection conn = new SqlConnection();
conn.setListener(listener);
count++;
return conn;
}
/**
* 初始化初始容量的数据库连接。
*/
public void init() {
for (int i = 0; i < initialCapacity; i++) {
connections.add(getNewConn());
}
}
public void setListener(SqlConnectTimeoutListener listener) {
this.listener = listener;
}
}
3、闲置超时监听。
package com.allin.servers.sql.pool;
public interface SqlConnectTimeoutListener {
public void connectTimeOut(SqlConnection conn);
}
4、数据库连接类,维持了一个数据库连接,主要是用于计算数据库连接的闲置时间。并在超时了通知数据库连接池。
package com.allin.servers.sql.pool;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Timer;
import java.util.TimerTask;
import com.allin.servers.sql.SqlConfig;
public class SqlConnection {
// 空闲时间计时器
Timer timer = new Timer();
// 数据库连接
Connection conn;
/**
* 用于命名的计数
*/
private static int count;
// 闲置时间超时监听器
SqlConnectTimeoutListener listener;
// 闲置超时时间
private final static long TIME_OUT = 1000 * 10;
// 计算超时的起始时间
private long start;
// 闲置时间
private long idleTime;
// 判断时候是处于闲置状态
private boolean isIdle;
// 连接名字
private String name;
private Task task;
/**
* 数据库连接构造方法,将在初始化数据库连接,调用者可以在该构造方法为连接命名 。
*
* @param name
*/
public SqlConnection(String name) {
connect();
this.name = name;
}
/**
* 数据库连接构造方法,将在初始化数据库连接,并将以默认的"SqlConnection-"加上一共建立的连接数作为连接名。
*/
public SqlConnection() {
this("SqlConnection-" + count++);
}
private void connect() {
try {
Class.forName(SqlConfig.jdbcDriverClassName);
conn = DriverManager.getConnection(SqlConfig.jdbcUrl,
SqlConfig.jdbcUsername, SqlConfig.jdbcPassword);
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 开始计时
*/
public void timing() {
task = (Task) getTask();
timer.schedule(task, TIME_OUT, TIME_OUT);
start = System.currentTimeMillis();
isIdle = true;
}
/**
* 移除计时
*/
public boolean cancelTimer() {
boolean isCancel = false;
if (isIdle) {
task.cancel();
isCancel = true;
}
isIdle = false;
return isCancel;
}
/**
* 得到数据库连接闲置时间,当数据库不是处于空闲时返回0
*
* @return 闲置时间
*/
public long getIdleTime() {
if (isIdle) {
idleTime = System.currentTimeMillis() - start;
return idleTime;
}
return 0;
}
private TimerTask getTask() {
return new Task(this);
}
protected void timeOut() {
if (listener != null) {
listener.connectTimeOut(this);
}
}
/**
* 结束数据库连接,释放资源
*/
public void close() {
try {
conn.close();
count--;
task.cancel();
timer.cancel();
} catch (SQLException e) {
e.printStackTrace();
} catch (NullPointerException e) {
}
}
/**
* 得到数据库连接
*
* @return
*/
public Connection getConn() {
return conn;
}
/**
* 设置数据库连接超时监听
*
* @param listener
*/
public void setListener(SqlConnectTimeoutListener listener) {
this.listener = listener;
}
/**
* 得到数据库闲置状态
*
* @return
*/
public boolean isIdle() {
return isIdle;
}
/**
* 得到设置的数据库连接名
*
* @return
*/
public String getName() {
return name;
}
/**
* 设置数据库连接类的名字.
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
}
class Task extends TimerTask {
SqlConnection conn;
Task(SqlConnection conn) {
this.conn = conn;
}
@Override
public void run() {
conn.timeOut();
}
}