自己手動寫代碼實現數據庫連接池


 

 正文前先來一波福利推薦:

 福利一:

百萬年薪架構師視頻,該視頻可以學到很多東西,是本人花錢買的VIP課程,學習消化了一年,為了支持一下女朋友公眾號也方便大家學習,共享給大家。

福利二:

畢業答辯以及工作上各種答辯,平時積累了不少精品PPT,現在共享給大家,大大小小加起來有幾千套,總有適合你的一款,很多是網上是下載不到。

獲取方式:

微信關注 精品3分鍾 ,id為 jingpin3mins,關注后回復   百萬年薪架構師 ,精品收藏PPT  獲取雲盤鏈接,謝謝大家支持!

-----------------------正文開始---------------------------

 

概念

池:一個高級的集合體(集合存儲元素 + 管理方式–>提高效率),是對外提供同一種類型對象的集合,如(線程池、數據庫連接池) 
特性:復用性(每條連接可重復使用),隔離性(每條連接一個時間內只能由一個線程獲取)!! 
連接池功能: 
1.最大限度的滿足並發復用,提高響應效率 
2.大大的節省了服務器資源,(一定程度上)減少大量初始化問題

代碼實現:

1.POOL Interface設計思想

一對外提供復用連接包裝內<—[pool]—>一對內創建連接

//抽取連接池架構接口
public interface MyPool {
//對外提供可復用連接包裝內
PooledConnection getConnection();
//對內創建連接
void createConnections(int count);
}

 

2.PooledConnection

為自定義連接池包裝類bean(原生的Connection沒有復用的標志,若不close回收掉,則不能判斷該connection是否在用情況) 
成員變量:

//表示繁忙標志     復用的標志 線程安全
private boolean busy = false;
//真正的sql 連接connection(java.sql.Connection)
private Connection con;
//只是用來測試當前connectionName,便於觀察
private String connName;

public boolean isBusy() {
return busy;
}

 
         

public void setBusy(boolean busy) {
this.busy = busy;
}

 
         

public Connection getCon() {
return con;
}

 
         

public void setCon(Connection con) {
this.con = con;
}

 
         

public String getConnName() {
return connName;
}

 
         

public void setConnName(String connName) {
this.connName = connName;
}

 

 

對外提供關閉方法

// 將該連接置為不可用,而不是真正關掉連接
public void close() {
    this.busy = false;
}

 

對外提供一個簡單的測試方法,也就是獲得了連接之后,就可以使用statement進行執行Sql語句;

public ResultSet queryBySql(String sql) {
    Statement sttm = null;
    ResultSet rs = null;
    try {
        sttm = con.createStatement();
        rs = sttm.executeQuery(sql);
        //System.out.println("當前連接編號是:" + connName);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return rs;
}

 

上面一個簡單的PooledConnection連接包裝類就完成了,主要功能就如之前所說的一樣,將DriverManager獲取的Connection進行包裝使其可復用(連接不是用完就關掉),隔離性由一個簡單的標志位屬性busy決定(當busy為false時,表明該連接空閑可用,為true則表示該連接已被使用)

3.MyPoolImpl(重點關鍵實現類)為Pool的實現類

功能: 
1.初始化數據連接driver 
2.初始化連接池容量(擴容容量) 
3.獲取連接單個連接 
成員變量

private static String driver = null;
private static String url = null;
private static String user = null;
private static String password = null;
/**連接池中管道參數**/
private static int initCount = 5;
private static int stepSize = 10;
private static int poolMaxSize = 55;
private static int expandTime = 0;
/**線程安全集合,用來放(可復用)數據庫連接管道(集合之前用Vector,但在測試的時候發現多線程並發出錯了ConcurrentModificationException)這個還要仔細研究一下**/
//連接池
private static CopyOnWriteArrayList<PooledConnection> pooledCons = new CopyOnWriteArrayList<PooledConnection>();

 

幾個重要實現方法 
1.注冊driver(根據配置文件properties文件修改初始化參數JdbcUtil操作)

private void init() {
    InputStream inStream = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");
    Properties properties = new Properties();
    try {
        properties.load(inStream);
    } catch (IOException e) {
        //若這里拋出異常則下面不運行
        e.printStackTrace();
    }
    this.driver = properties.getProperty("jdbc_driver");
    this.url = properties.getProperty("jdbc_url");
    this.user = properties.getProperty("jdbc_username");
    this.password = properties.getProperty("jdbc_password");
    if(Integer.valueOf(properties.getProperty("initCount")) > 0) {
        this.initCount = Integer.valueOf(properties.getProperty("initCount"));
    }else if(Integer.valueOf(properties.getProperty("stepSize")) > 0) {
        this.stepSize = Integer.valueOf(properties.getProperty("stepSize"));
    }else if(Integer.valueOf(properties.getProperty("poolMaxSize")) > 0) {
        this.poolMaxSize = Integer.valueOf(properties.getProperty("poolMaxSize"));
    }
    //准備創建DriverManager
    try {
        Driver dbDriver = (Driver) Class.forName(this.driver).newInstance();
        DriverManager.registerDriver(dbDriver);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    //獲取連接,用create方法獲取
    /**DriverManager.getConnection(url, user, password);**/
    this.createConnections(initCount);
    }

2.初始化連接池容量(也就是實現Pool Interface的方法創建連接連接池) 
所有的Console都是為了便於觀察

 
        
@Override
public void createConnections(int count) {
    //this.expandTime++;
    //System.out.println("第"+expandTime+"次擴容,擴容量為:"+count);
    if((pooledConnections.size() + count) <= poolMaxSize) {
        for(int i = 0;i < count ;i++) {
            try {
                //獲取連接放入線程安全的連接池中
                Connection conn = DriverManager.getConnection(url, user, password);
                PooledConnection pooledConnection = new PooledConnection(conn,false,String.valueOf(i));
                this.pooledConnections.add(pooledConnection);
                //System.out.println("初始化"+(i + 1) + "個連接");
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    System.out.println("當前連接池連接數量:"+ pooledConnections.size());
    System.out.println("最大連接池數量為:"+ this.poolMaxSize);
}

 

 
        

3.1 對外提供獲取連接包裝類(也是實現Interface的方法)

@Override
public PooledConnection getConnection() {
    //spring思想要拿連接,先判斷管道集合中是否有連接
    if(this.pooledConnections.size() == 0) {
        System.out.println("連接池沒有連接!");
        //如果沒有就手動再建一把連接池
        this.createConnections(initCount);
    }
    PooledConnection connection = getRealConnection();
    //如果還是沒有拿到,說明全部線程都處於busy狀態,得擴容
    while(connection == null) {
        this.createConnections(stepSize);
        connection = getRealConnection();
        try {//拿到連接等待一會,防止連接又被別的線程搶奪
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return connection;
}
 

3.2 真正的獲取連接包裝類getRealConnection()

//同步方法,真正的獲取連接(連接包裝內包括:connection和標志位busy)
private synchronized PooledConnection getRealConnection() {
    for(PooledConnection conn:pooledConnections) {
        //判斷該連接是否已被占用
        if(!conn.isBusy()) {//false為可用(空閑),true為占用(繁忙)
            Connection connection = conn.getConnection();
            try {
                //判斷該連接是否在設定時間連接通數據庫(連接通為true)
                if(!connection.isValid(2000)) {
                    connection = DriverManager.getConnection(url, user, password);
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            conn.setBusy(true);
            return conn;
        }
    }
    //如果連接池中的連接都被占用,則返回null,由調用函數處理(即擴容)
    return null;
}

 

 

以上連接池實現大致功能就完成了,主要包涵初始化注冊,連接池擴容和獲取連接方法

4.連接池管理類對象的封裝PoolManager
public class PoolManager {
private PoolManager(){}
/**
 * 內部類單利模式 偽造類加載器加載原理:實現線程安全問題(類加載器加載內部類實例是互斥特性)
 */
private static class createPool {
    private static MyPoolImpl poolImpl = new MyPoolImpl();
}
public static MyPool getInstace() {
    return createPool.poolImpl;
}
}

 

特性:每個線程對類加載內部類實例時是互斥

測試

 

 

測試類Test.class 
測試主方法

//測試2000條線程 jdk1.8 內部類用lambda表達示
public static void main(String[] args) {
    for(int i =0; i < 2000; i++) {
        new Thread(() -> selectDate()).start();
    }
}

 

 

測試方法selectDate()

成員變量
public static MyPool myPool = PoolManager.getInstace();
//方法加了synchronized之后,連接池不擴容了???
public static void selectDate() {       
    PooledConnection connection = myPool.getConnection();
    String sql = "select * from t_area";
    ResultSet rs = connection.queryBySql(sql);
    try {
        while(rs.next()) {
            String name = rs.getString("name");
            Integer id = rs.getInt("id");
            //System.out.println("當前線程:"+ Thread.currentThread().getName() +",id:"+ id + ",name" + name);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    connection.close();
}

 

 

 

這里寫圖片描述


問題: 
1.myPoolImpl類中連接池的集合問題用線程安全的Vector,在多次測試中出現Java ConcurrentModificationException 異常Java ConcurrentModificationException異常原因和解決方法 
2.當在測試方法selectDate()上加了synchronized之后,連接池不擴容問題???,相關鎖問題還不夠了解 
3.運行觀測問題:擴容的連接數量遠遠大於了實際運行使用的數量(基本上就是0-10號連接在用)??? 
這里寫圖片描述 


免責聲明!

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



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