JDBC連接池的簡單實現


實現一個簡單的數據庫連接池

 

前言:

  最近在搞一個項目,就是要把SqlServer數據全部導入到Oracle中,也要讓項目由原來的SqlServer支持Oracle,於是自已在網上找了很多工具,發現導的時候都有問題,而且數據量非常龐大。一開始是自已手動導,將SqlServer數據庫導成支持Oracle的sql文件,然后再把這個sql文件導入到Oracle中,發現將一個10萬條的sql文件導入到Oracle中都要半小時,簡直崩潰了!  想想單個導sql文件的方式屬於單線程模式,因為使用PLSQL工具導是只有一個連接,於是就想到了使用多線程的方式導,也就是采用多個連接,多個子任務去導。因此便使用到了資源池的這種模式。使用多線程、資源池方式之后,速度提升了上千倍。

 

實現思路(如下圖所示): 

 

說明:

使用一個池也可實現資源池(即Map<Connetion, Params> pool 這種方式)但是這種邏輯復雜一點,即一個pool中要保證不連接數不能超過最大值又要判斷哪些連接已經被占用。而我這里采用兩個池來實現,一個是Used池,用來存放正在連接的資源;一個是Pool池,用來存放已經釋放連接的資源;這樣邏輯清晰簡單。

 

實現步驟: 

下面就是數據庫連接池的簡單實現方式:

 

1.編寫一個對象池的抽象類

package com.core.jdbc;


import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

public abstract class ObjectPool<T> {

    /**
     * 創建對象
     */
    protected abstract T create();

    /**
     * 驗證對象有效性
     */
    public abstract boolean validate(T t, long createTime);

    /**
     * 使對象失效
     */
    public abstract void expire(T t);

    private ConcurrentHashMap<T, Long> used; // 正在使用的,保存新建的資源
    private ConcurrentHashMap<T, Long> pool; // 未被使用的,保存釋放的、未失效的資源,池子里面保存有效可用的對象

    private static int MAX_CONNECT_SIZE = 100;         // 最大連接數(這里也是pool的最大size,雖然沒有定義pool的最大size,但是從整個邏輯上講,pool的size是小於這個值的)
    public static int MAX_KEEP_ALIVE_TIME = 3000;      // 最大生存時間,這個時間也決定了創建連接的頻率

    /**
     * 獲得資源
     */
    public synchronized T getResource() {
        T t = null;
        // 初始化
        if (null == pool) {
            pool = new ConcurrentHashMap<T, Long>();
        }
        if (null == used) {
            used = new ConcurrentHashMap<T, Long>();
        }
        
        // 超過最大連接數,等待(注意:這里可以加一個拒絕策略,即超時拒絕連接還是繼續等待)
        while (used.size() >= MAX_CONNECT_SIZE) {
            try {
                this.wait(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } 
        
        // 默認先從池子里面去取
        if (pool.size() > 0) {
            for (Entry<T, Long> entry : pool.entrySet()) {
                t = entry.getKey();
                if (null!=t) {
                    break;
                }
            }
            pool.remove(t);
        } 
        // 在池子中未取到,創建一個新的
        if (null == t) {
            t = create();
        }
        used.put(t, System.currentTimeMillis());
        this.notifyAll();
        return t;
    }

    /**
     * 釋放某個資源
     */
    public synchronized void release(T t) {
        if (null==t) {
            return;
        }
        
        while (used.size()==0) {
            try {
                this.wait(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 判斷是否過有效期
        if (validate(t, used.get(t))) {
            // 放入池中
            pool.put(t, System.currentTimeMillis());
            used.remove(t);
        } else {
            expire(t);
            used.remove(t);
        }
        
        this.notifyAll();
    }

}

 

2.編寫數據庫連接池的具體實現類

public class ConnectionPool extends ObjectPool<Connection> {
    private static int count = 0;

    public ConnectionPool() {
        try {
            Class.forName("oracle.jdbc.driver.OracleDriver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected Connection create() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection("jdbc:oracle:thin:@//127.0.0.1:1521/SZNY", "caoxiaobo", "123456");
        } catch (SQLException e) {
            e.printStackTrace();
        }
        count ++;
        logger.debug("建立連接次數" +count);
        return conn;
    }

    @Override
    public boolean validate(Connection o, long createTime) {
        if (System.currentTimeMillis() - createTime > MAX_KEEP_ALIVE_TIME)
            return false;
        return true;
    }

    @Override
    public void expire(Connection o) {
        try {
            o.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

 

 

3.編寫JDBC連接池單例類來確保只有一個池(確保ConnectionPool 對象唯一,即程序中所有的連接都從一個pool中去取)

public class JdbcConnection {

    private JdbcConnection () {
        
    }
    
    private static class Singleton {
        private static ConnectionPool pool = new ConnectionPool();
    }
    
    public static Connection getConnection() {
        return Singleton.pool.getResource();
    }

    public static void release(Connection conn) {
        Singleton.pool.release(conn);
    }

}

 

 

4.並發測試類:

public class PoolTest {
    
    public static void main(String[] args) {

        Runnable run = new Runnable() {
            
            @Override
            public void run() {
                Connection conn = JdbcConnection.getConnection();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                JdbcConnection.release(conn);
            }
        };
     // 創建2000個線程,模擬並發
for (int i=0; i<2000; i++) { Thread thread = new Thread(run); thread.start(); } } }

 

測試結果:

假設並發請求有2000個(假設數據庫最大連接數為150,這里設置要比它正常值小一點,即100),如果不使用資源池,那么就需要不斷的創建、銷毀2000次連接,對於服務器的性能來說影響還是比較大的。通過這個示例,我們可以看到這個結果(創建、銷毀)遠遠小於2000次,大概測試了一下平均100-120之間。當然這里的這個值是根據它設定的生存時間來決定的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM