咱們都知道數據庫鏈接是一種有限和很是昂貴的應用資源,怎樣對這些資源進行高效的管理,能有效的改善整個系統的性能和健壯性。數據庫鏈接池正是針對這個問題而提出來的。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