資料庫連線池認知+手寫一個
阿新 • • 發佈:2018-11-10
首先推薦一篇不錯的文章。https://www.cnblogs.com/newpanderking/p/3875749.html
接下來,看了這篇文章之後,我們再看一下
1.首先是連線池的規範
/** * 連線池 頂級規範 */ public interface IPool { /** * 獲取連線 */ MyConn getConnection(); } /** * 定義了一定規範的抽象類 */ /** * * 連線池 超類:用以提供部分規範 */ public abstract class BasePool implements IPool{ protected String jdbcDriver; protected String dbUrl; protected String dbUsername; protected String dbPassword; protected int maxConnnect; protected int initedConnect; protected List<MyConn> conns; public BasePool(String jdbcDriver, String dbUrl, String dbUsername, String dbPassword, int maxConnnect,int firstInit){ //我本來要放棄這個初始化引數:int firstInit。 //其本來是決定new多少個connection,來減少啟動的消耗。 //因為第一次優化後產生的bug主要是在於多執行緒下init<max時,生成了更多的連線導致的問題。 //但是我看見阿里的druid都有這個引數, //就嘗試了一下將createConn(int)這個方法加上鎖(synchronized)或者lock都可以。 //這個地方其實應該也可以採用volatile關鍵字 //但是其實createConn(int)這個方法應該競爭並沒有那麼激烈,不用強行在這個地方追求極致的效率 this.jdbcDriver = jdbcDriver; this.dbUrl = dbUrl; this.dbUsername = dbUsername; this.dbPassword = dbPassword; this.maxConnnect = maxConnnect; //是為了保證執行緒安全。但是這樣做,效率會降低很多,後續想個方式來處理 //哈哈 解決了,但是還是把最初的版本放在這裡 //conns=new Vector<>(new MyConn[initedConnect]); conns=new ArrayList<>(maxConnnect); //建立池 initPool(firstInit); //建立守護執行緒 new Thread(new GuardConnection(this,4)).start();; } protected void initPool(int initedConnect) { try { Class.forName(jdbcDriver); createConnections(initedConnect); }catch (Exception e) { throw new RuntimeException("建立資料庫連接出錯"); } } protected void createConnections(int count) { for (int i = 0; i < count; i++) { // 大於最大連線數的話就不建立了 if (initedConnect + 1 > maxConnnect) return; if (count <= 0) throw new RuntimeException("初始化連線數不能為空"); try { Connection conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword); conns.add(new MyConn(conn, false)); // 已載入的連線數量++ initedConnect++; } catch (SQLException e) { e.printStackTrace(); } } } public abstract List<MyConn> getConns(); } /** * 連線池例項 */ public class MyPool extends BasePool { public MyPool(String jdbcDriver, String dbUrl, String dbUsername, String dbPassword, int maxConnnect, int initedConnect) { super(jdbcDriver, dbUrl, dbUsername, dbPassword, maxConnnect, initedConnect); } @Override public MyConn getConnection() { // 判斷是否載入 if (initedConnect == 0) { System.out.println("連線池中還沒有連線!"); throw new RuntimeException("連線池中還沒有連線!"); } /** * (1)當還有可用連線的時候 * (2)可用連線不夠了,需要建立或者等待 */ MyConn conn = getActiveConnection(); // (2)的情況 while (null == conn) { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } //建立連線 createConnections(1); conn = getActiveConnection(); } return conn; } /** * 從集合中得到連線 * * @return * @return */ private MyConn getActiveConnection() { for (int i = 0; i < conns.size(); i++) { MyConn conn = conns.get(i); if(conn.setBusy(true)) { conn.setUseTime(System.currentTimeMillis()); return conn; } } return null; } @Override public List<MyConn> getConns() { return super.conns; } }
2.connection的包裝物件
/** * 包含conn的物件 */ public class MyConn { /** * 實際connection */ private Connection conn; /** * 是否正在工作 * 剛建立時,預設是false。 * volatile關鍵字,是為了提高效率,將陣列的鎖定放在了每一個元素的鎖定 * 就有點類似與conCurrentHashMap。 * 後面發現即使使用了volatile關鍵字還是會存在問題。 * 因此後面還是使用了synchronized關鍵字來控制,但是即使如此,還是講粒度放在了每個元素上。 * 從而提高了效能 */ private volatile boolean isBusy=false; /** * 開始使用的時間:用於守護執行緒去強制回收 */ private long useTime; /** * 查詢 * @param sql * @return */ public ResultSet query(String sql) { Statement sm = null; ResultSet rs = null; try { sm = conn.createStatement(); rs = sm.executeQuery(sql); } catch (SQLException e) { e.printStackTrace(); } return rs; } /** * 修改操作 * @param sql * @return */ public int update(String sql) { Statement sm = null; int count = -1; try { sm = conn.createStatement(); count = sm.executeUpdate(sql); } catch (SQLException e) { e.printStackTrace(); } return count; } public boolean isBusy() { return isBusy; } /** * 當傳入的是true的時候,說明,準備佔用 * 但是外面用的list不安全,因此考慮在set層操作, * 這裡的返回值是為了確定獲取成功 */ public boolean setBusy(boolean isBusy) { //本來if else這種東西應該抽象出來,但是目前先這樣吧 synchronized(this) { if(isBusy) { if(this.isBusy) { return !isBusy; } } //false的時候無所謂(後面思考是否會有執行緒安全的問題) this.isBusy=isBusy; return isBusy; } } public void close() { this.isBusy=false; } public MyConn(Connection conn, boolean isBusy) { super(); this.conn = conn; this.isBusy = isBusy; } public long getUseTime() { return useTime; } public void setUseTime(long useTime) { this.useTime = useTime; } public void rollback() throws SQLException { conn.rollback(); } }
3.守護執行緒
/** * connection守護執行緒 */ public class GuardConnection implements Runnable{ private BasePool pool; private long time; @Override public void run() { while(true) { for(int i=0;i<pool.getConns().size();i++) { MyConn conn = pool.getConns().get(i); if(conn.isBusy() && System.currentTimeMillis() - conn.getUseTime() > time*1000 ) { try { conn.rollback(); }catch (Exception e) { }finally { conn.close(); } } } try { Thread.sleep(3000); } catch (InterruptedException e) { } } } public GuardConnection(BasePool pool,long time) { super(); this.pool = pool; this.time=time; } }
這樣就能夠完成一個簡單的連線池。但是還有很多東西沒有考慮。後面慢慢更新,一步步優化,加上功能:例如connection的守護執行緒,強制回池,效能等方面的。
---------------------------------------------------------------------------------------------------------------------------------------
目前,強制回池,守護執行緒都已經做到了,效能方面,經過測試,確實是有所提升。但是還是存在一些不足,例如可供拓展的一些AOP功能等等。後續考慮一下