咱们都知道数据库链接是一种有限和很是昂贵的应用资源,怎样对这些资源进行高效的管理,能有效的改善整个系统的性能和健壮性。数据库链接池正是针对这个问题而提出来的。html
数据库链接负责分配、释放和管理数据库链接。使数据库链接能够重复利用,而不是用一次创建一次数据库链接。java
基本思路算法
创建一个容器sql
每次到这个容器里获得链接,若是为空则创建一个新链接。数据库
当链接使用完后归还给这个容器api
这里就有二个难点安全
1. 容器必需是同步的,线程安全的。并发
2. 链接怎归还链接池ide
方案:性能
针对这二个难点,咱们分别提出了二个解决方法
1.使用ConcurrentLinkedQueue实现先进先出队列
ConcurrentLinkedQueue无界线程安全队列介绍
这个类在java.util.concurrent包中,咱们来看看官方是怎描述这个类的
一个基于连接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。新的元素插入到队列的尾部,队列获取操做从队列头部得到元素。当多个线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不容许使用 null 元素.此实现采用了有效的“无等待 (wait-free)”算法
2.动态代理实现链接归还链接池
你们也能够参考刘冬在IBM发表的文章
http://www.ibm.com/developerworks/cn/java/l-connpoolproxy/
接下来咱们来看看总体代码
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import javax.sql.DataSource;
public class JavaGGDataSource implements DataSource {
//链接队列
private ConcurrentLinkedQueue<_Connection> connQueue = new ConcurrentLinkedQueue<_Connection>();
//存放全部链接容器
private List<_Connection> conns = new ArrayList<_Connection>();
private Driver driver = null;
private String jdbcUrl = null;
private String user = null;
private String password = null;
private int maxActive = -1;// -1为不限制链接数
private String driverClass = null;
private int timeout = 1000 * 60 * 60 * 4;// 默认为4小时,即4小时没有任何sql操做就把全部链接从新创建链接
private AtomicLong lastCheckout = new AtomicLong(System.currentTimeMillis());
private AtomicInteger connCount = new AtomicInteger();
//线程锁,主要用于新建链接和清空链接时
private ReentrantLock lock = new ReentrantLock();
public void closeAllConnection() {
}
/**
* 归还链接给链接池
*
* @param conn
*@date 2009-8-13
*@author eric.chan
*/
public void offerConnection(_Connection conn) {
connQueue.offer(conn);
}
@Override
public Connection getConnection() throws SQLException {
return getConnection(user, password);
}
/**
* 从池中获得链接,若是池中没有链接,则创建新的sql链接
*
* @param username
* @param password
* @author eric.chan
*/
@Override
public Connection getConnection(String username, String password)
throws SQLException {
checkTimeout();
_Connection conn = connQueue.poll();
if (conn == null) {
if (maxActive > 0 && connCount.get() >= maxActive) {
for (;;) {// 采用自旋方法 从已满的池中获得一个链接
conn = connQueue.poll();
if (conn != null)
break;
else
continue;
}
}
lock.lock();
try {
if (maxActive > 0 && connCount.get() >= maxActive) {
// 处理并发问题
return getConnection(username, password);
}
Properties info = new Properties();
info.put("user", username);
info.put("password", password);
Connection conn1 = loadDriver().connect(jdbcUrl, info);
conn = new _Connection(conn1, this);
int c = connCount.incrementAndGet();// 当前链接数加1
conns.add(conn);
System.out.println("info : init no. " + c + " connectioned");
} finally {
lock.unlock();
}
}
lastCheckout.getAndSet(System.currentTimeMillis());
return conn.getConnection();
}
/**
* 检查最后一次的链接时间
*
* @throws SQLException
*@date 2009-8-13
*@author eric.chan
*/
private void checkTimeout() throws SQLException {
long now = System.currentTimeMillis();
long lt = lastCheckout.get();
if ((now - lt) > timeout) {
_Connection conn = null;
lock.lock();
try {
if(connCount.get()==0)return;
while ((conn = connQueue.poll()) != null) {
System.out.println("connection " + conn + " close ");
conn.close();
conn = null;
}
for(_Connection con:conns){
con.close();
}
conns.clear();
System.out.println("info : reset all connections");
connCount.getAndSet(0);// 重置链接数计数器
lastCheckout.getAndSet(System.currentTimeMillis());
} finally {
lock.unlock();
}
}
}
/**
*
* @return
*@date 2009