用JAVA實現無等待數據庫鏈接池

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

相關文章
相關標籤/搜索