数据库连接池是我们服务器端常用的减少开销的手段,通常配合线程池一起使用。那么今天我们就来尝试实现数据库连接池。
先说一下实现逻辑:毋庸置疑,我们通过单例拿到对象,在第一次创建对象时会创建一定数量的Connetion连接线,并且可以通过getConnection方法拿到连接,用完之后通过release进行回收,大体的逻辑其实很简单,但是这可是用来操作数据库的,所以我们在内部必须要做各种安全处理。比如当池中Connection耗尽时,并且新建数量也达到最大,那么我们就用synchronized处理,并等待。如果拿到的Connection不可用,那么就通过递归继续获取等。好,接下来看代码。
public class ConnectionPool {
/*线程安全数组*/
private volatile Vector<Connection> pool;
private volatile Connection selectConnection;
/*公有属性*/
private String url = "jdbc:mysql://127.0.0.1/text";
private String username = "root";
private String password = "root";
private String driverClassName = "com.mysql.jdbc.Driver";
/*连接池中储存的最大数量*/
private final int poolSize = 10;
/*当线程池耗尽时,最大新建数量*/
private final int newPoolSize = 10;
private volatile static int tab = 0;
private static ConnectionPool instance = null;
private Connection conn = null;
private static ConnectionPool mConnectionPool = null;
public static ConnectionPool getInstance() throws SQLException{
synchronized (Connection.class) {
if (mConnectionPool == null){
mConnectionPool = new ConnectionPool();
return mConnectionPool;
}
return mConnectionPool;
}
}
/*构造方法,做一些初始化工作*/
private ConnectionPool() throws SQLException {
pool = new Vector<Connection>(poolSize);
selectConnection = DriverManager.getConnection(url, username, password);
try {
for (int i = 0; i < poolSize; i++) {
conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/*得到select使用的连接,查询不同于其他三项,查询可以并发进行执行,
* 所以我们只需提供一个连接来供所有用户使用*/
public Connection getSelectConnection() throws SQLException{
if(selectConnection != null){
return selectConnection;
}
synchronized (ConnectionPool.class) {
if(selectConnection == null){
selectConnection = DriverManager.getConnection(url, username, password);
}
}
return selectConnection;
}
/* 返回连接到连接池
* 在这里进行控制,如果连接池里的连接数大于我们规定的数量,则对此连接进行关闭
*/
public synchronized void release(Connection con) throws SQLException {
if(con == null){
return;
}
if (pool.size() >= poolSize){
tab-=1;
con.close();
return;
}
pool.add(con);
}
/* 返回连接池中的一个数据库连接
* 如果连接池中已经耗尽了Connextion
* 则创建新的使用
*/
public Connection getConnection() {
synchronized(ConnectionPool.class){
if (pool.size() > 0) {
Connection conn = pool.get(0);
pool.remove(conn);
System.out.println("得到连接");
return conn;
}
if(tab < newPoolSize){
System.out.println("得到新建的连接");
return newConnection();
}
}
/*如果连接池耗尽,并且新建连接也到最大值,那么在这里排队等待*/
synchronized (ConnectionPool.class) {
try {
while(pool.size() == 0){
Thread.sleep(1*50);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Connection conn = pool.get(0);
pool.remove(conn);
System.out.println("得到回收之后重新利用的连接");
return conn;
}
}
/*新建连接*/
private Connection newConnection(){
try {
tab+=1;
Connection conn = DriverManager.getConnection(url, username, password);
pool.add(conn);
return conn;
} catch (SQLException e) {
return null;
}
}
}
OK,到这里就结束了,顺便提一下,在连接池的连接耗尽时,并且新建数量也达到最大,导致用户等待,在这里建议使用观察者模式进行处理!可能有细心的朋友已经发现我这里并没有加载驱动:Class.forName(driverClassName); 其实在java 6 之后我们不用显式的加载驱动了,DriverManager 会自动从当前在 CLASSPATH 中的驱动文件中找到,当然驱动包必不可少的。