衆所周知,Java的Object對象提供的,wait()和notify()/notifyAll()等接口是併發編程的重要組成部分。它們對多線程之間的協做起了很是重要的做用,實際開發中也有不少場景能夠採用。廢話少說,今天咱們就用此機制來模擬實現一個jdbc支持等待超時模式的鏈接池。數據庫
1、模擬實現一個數據庫鏈接接口
編程
//類說明:空實現一個Connection接口(由於重點不在這裏,因此如下接口中的方法只作簡單處理) public class SqlConnectImpl implements Connection{ /*拿一個數據庫鏈接*/ public static final Connection fetchConnection(){ return new SqlConnectImpl(); } @Override public boolean isWrapperFor(Class<?> arg0) throws SQLException { // TODO Auto-generated method stub return false; } //由於重點不在這裏,因此這裏省略其它接口... }
2、實現數據庫等待超時鏈接池的核心方法多線程
//類說明:鏈接池的實現 DBPool { //模擬:數據庫鏈接池 LinkedList<Connection> pool = LinkedList<Connection>()(initialSize) { if(initialSize > ) { for(int i = 0;i < initialSize; i++) { pool.addLast(SqlConnectImpl.fetchConnection()); } } } //鏈接池:釋放鏈接,通知其餘線程 public void releaseConnection(Connection connection) { if (connection != null) { synchronized (pool){ pool.addLast(connection); pool.notifyAll(); } } } //鏈接池:獲取鏈接使用 public Connection fetchConnection(long mills) throws InterruptedException { synchronized (pool){ //未設置超時,直接獲取 if(mills <0){ while (pool.isEmpty()){ pool.wait(); } return pool.removeFirst(); } //設置超時 long future = System.currentTimeMillis()+mills;/*超時時刻*/ long remaining = mills; while (pool.isEmpty() && remaining > 0){ pool.wait(remaining); //喚醒一次:從新計算等待時長 remaining = future - System.currentTimeMillis(); } Connection connection = null; if(!pool.isEmpty()){ connection = pool.removeFirst(); } return connection; } } }
3、多線程併發模式下對鏈接池的訪問併發
//類說明:數據庫鏈接池測試類 public class DBPoolTest { static DBPool pool = new DBPool(10); // 控制器:控制main線程將會等待全部Woker結束後才能繼續執行 static CountDownLatch end; public static void main(String[] args) throws Exception { // 線程數量 int threadCount = 50; end = new CountDownLatch(threadCount); int count = 20;//每一個線程的操做次數 AtomicInteger got = new AtomicInteger();//計數器:統計能夠拿到鏈接的線程 AtomicInteger notGot = new AtomicInteger();//計數器:統計沒有拿到鏈接的線程 for (int i = 0; i < threadCount; i++) { Thread thread = new Thread(new Worker(count, got, notGot), "worker_"+i); thread.start(); } end.await();// main線程在此處等待 System.out.println("總共嘗試了: " + (threadCount * count)); System.out.println("拿到鏈接的次數: " + got); System.out.println("沒能鏈接的次數: " + notGot); } static class Worker implements Runnable { int count; AtomicInteger got; AtomicInteger notGot; public Worker(int count, AtomicInteger got, AtomicInteger notGot) { this.count = count; this.got = got; this.notGot = notGot; } public void run() { while (count > 0) { try { // 從線程池中獲取鏈接,若是1000ms內沒法獲取到,將會返回null // 分別統計鏈接獲取的數量got和未獲取到的數量notGot Connection connection = pool.fetchConnection(1000); if (connection != null) { try { connection.createStatement(); connection.commit(); } finally { pool.releaseConnection(connection); got.incrementAndGet(); } } else { notGot.incrementAndGet(); System.out.println(Thread.currentThread().getName() +"等待超時!"); } } catch (Exception ex) { } finally { count--; } } end.countDown(); } } }
4、測試結果報告app
5、結束語ide
總結:1) 對wait()、notify()、notifyAll()等方法使用時須用,synchronized關鍵包裹(對象、方法或塊)都可, 不包裹運行中必報錯;測試
2) 線程在執行wait()方法在會自動釋放持有的鎖;fetch
3) 線程在執行notify()或notifyAll()後,不會當即釋放該線程持有的鎖資源,只有在synchronized包裹的語句塊或方法執行完畢後纔會釋放;this
4) 採用notify()方法喚醒時只會隨機喚醒一個線程,在多喚醒條件下不適用此方法。推薦使用notifyAll()喚醒全部與鎖對象相關的全部線程;spa