正文前先來一波福利推薦:
福利一:
百萬年薪架構師視頻,該視頻可以學到很多東西,是本人花錢買的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號連接在用)???