JDK5新特性之線程同步工具類(三)
一. Semaphore
Semaphore能夠控制同一時候訪問資源的線程個數, 比如: 實現一個文件同意的並發訪問數.
Semaphore實現的功能就類似廁全部5個坑, 增加有十個人要上廁所, 那麽同一時候僅僅能有5個人能夠占用, 當5個人中的不論什麽一個人離開後, 當中在等待的另外5個人中就有一個能夠占用了. 另外等待的5個人中能夠是隨機獲得優先機會, 也能夠使依照先來後到的順序獲得機會, 這取決於構造Semaphore對象時傳入的參數選項.
public class SemaphoreTest { public static void main(String[] args) { // 創建一個線程池 ExecutorService service = Executors.newCachedThreadPool(); final Semaphore sp = new Semaphore(3); // 表示當前有3盞燈(同意3個並發) // 啟動5個線程 for (int i = 0; i < 5; i++) { service.execute(new Runnable() { public void run() { try { sp.acquire(); // 點亮一盞燈 // availablePermits: 表示能夠使用的燈 System.out.println("線程" + Thread.currentThread().getName() + " 進入,當前已有" + (3 - sp.availablePermits()) + "個並發"); Thread.sleep((long) (Math.random() * 10000)); System.out.println("線程" + Thread.currentThread().getName() + " 即將離開"); sp.release(); // 熄滅一盞燈(釋放) System.out.println("線程" + Thread.currentThread().getName() + " 已離開,當前已有" + (3 - sp.availablePermits()) + "個並發"); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } }
線程pool-1-thread-1 進入,當前已有1個並發 線程pool-1-thread-2 進入,當前已有2個並發 線程pool-1-thread-4 進入,當前已有3個並發 線程pool-1-thread-4 即將離開 線程pool-1-thread-4 已離開。當前已有2個並發 線程pool-1-thread-3 進入。當前已有3個並發 線程pool-1-thread-2 即將離開 線程pool-1-thread-2 已離開,當前已有2個並發 線程pool-1-thread-5 進入。當前已有3個並發 線程pool-1-thread-5 即將離開 線程pool-1-thread-5 已離開,當前已有2個並發 線程pool-1-thread-1 即將離開 線程pool-1-thread-1 已離開,當前已有1個並發 線程pool-1-thread-3 即將離開 線程pool-1-thread-3 已離開,當前已有0個並發
二. CyclicBarrier
表示大家彼此等待。集合好後才開始出發,分散活動後又在指定地點集合碰面。
這就好比整個公司的人員裏利用周末時間集體郊遊一樣。先各自從家出發到公司集合後,再同一時候出發到公園遊玩,在指定地點集合後再同一時候開始就餐。
public class CyclicBarrierTest { public static void main(String[] args) { // 開啟一個線程池 ExecutorService executorService = Executors.newCachedThreadPool(); // 參數3: 表示有3個到齊了才幹夠往下走。否則一直處於等待狀態 final CyclicBarrier cb = new CyclicBarrier(3); // 創建3個線程 for(int i = 0; i < 3; i++){ executorService.execute(new Runnable(){ @Override public void run(){ try { Thread.sleep((long)(Math.random() * 1000)); // 每一個線程“歇息”的時間不同 System.out.println("線程" + Thread.currentThread().getName() + "即將到達集合地點1。當前已有" + (cb.getNumberWaiting() + 1) + "個已經到達," + (cb.getNumberWaiting() == 2 ? "都到齊了,繼續前進" : "正在等候")); cb.await(); // 先到的等待後到的,當3個都到達時才會繼續向下運行 Thread.sleep((long)(Math.random() * 1000)); // 每一個線程“歇息”的時間不同 System.out.println("線程" + Thread.currentThread().getName() + "即將到達集合地點2。當前已有" + (cb.getNumberWaiting() + 1) + "個已經到達," + (cb.getNumberWaiting() == 2 ? "都到齊了。繼續前進" : "正在等候")); cb.await(); Thread.sleep((long)(Math.random()*1000)); // 每一個線程“歇息”的時間不同 System.out.println("線程" + Thread.currentThread().getName() + "即將到達集合地點3,當前已有" + (cb.getNumberWaiting() + 1) + "個已經到達。"+ (cb.getNumberWaiting() == 2 ? "都到齊了。繼續前進" : "正在等候")); cb.await(); } catch (Exception e) { e.printStackTrace(); } } }); } executorService.shutdown(); } }
線程pool-1-thread-2即將到達集合地點1,當前已有1個已經到達,正在等候 線程pool-1-thread-3即將到達集合地點1,當前已有2個已經到達,正在等候 線程pool-1-thread-1即將到達集合地點1,當前已有3個已經到達。都到齊了,繼續前進 線程pool-1-thread-2即將到達集合地點2。當前已有1個已經到達。正在等候 線程pool-1-thread-3即將到達集合地點2,當前已有2個已經到達。正在等候 線程pool-1-thread-1即將到達集合地點2。當前已有3個已經到達。都到齊了。繼續前進 線程pool-1-thread-3即將到達集合地點3,當前已有1個已經到達。正在等候 線程pool-1-thread-2即將到達集合地點3,當前已有2個已經到達,正在等候 線程pool-1-thread-1即將到達集合地點3,當前已有3個已經到達,都到齊了,繼續前進
三. CountDownLatch
宛如倒計時計數器, 調用CountDownLatch對象的countDown方法就將計數器減一, 當計數到達0時, 則全部等待者或單個等待者開始運行.
應用: 裁判一聲口令, 運動員同一時候開始奔跑, 當全部運動員都跑到終點後裁判發布結果. 還能夠實現一個計劃須要多個領導都簽字後才幹繼續向下實施的情況.
public class CountDownLatchTest { public static void main(String[] args) throws Exception { ExecutorService service = Executors.newCachedThreadPool(); // 子計數器, count為1 final CountDownLatch subCounter = new CountDownLatch(1); // 主計數器, count為3 final CountDownLatch mainCounter = new CountDownLatch(3); for(int i = 0; i < 3; i++){ service.execute(new Runnable() { @Override public void run() { try { System.out.println("線程 "+ Thread.currentThread().getName()+"正準備接受命令。"); subCounter.await(); // 子線程等待 System.out.println("線程 "+ Thread.currentThread().getName()+"已接受命令!"); Thread.sleep((long)Math.random() * 10000); System.out.println("線程 "+ Thread.currentThread().getName()+"回應命令處理結果!"); mainCounter.countDown(); // 將計數器身上的計數減1, 當計數為0時, 主線程將開始運行 } catch (Exception e) { e.printStackTrace(); } } } ); } Thread.sleep((long)Math.random() * 1000); System.out.println("線程 "+ Thread.currentThread().getName()+"即將公布命令!"); subCounter.countDown(); // 將計數器身上的計數減1, 當計數為0時, 子線程開始運行 System.out.println("線程 "+ Thread.currentThread().getName()+"已發送命令,正在等待結果。"); mainCounter.await(); // 主線程等待 System.out.println("線程 "+ Thread.currentThread().getName()+"已收到全部響應結果!
"); service.shutdown(); } }
線程 pool-1-thread-1正準備接受命令。 線程 pool-1-thread-3正準備接受命令! 線程 pool-1-thread-2正準備接受命令!線程 main即將公布命令!
線程 main已發送命令。正在等待結果!
線程 pool-1-thread-2已接受命令! 線程 pool-1-thread-3已接受命令。 線程 pool-1-thread-1已接受命令。 線程 pool-1-thread-3回應命令處理結果! 線程 pool-1-thread-2回應命令處理結果! 線程 pool-1-thread-1回應命令處理結果。 線程 main已收到全部響應結果!
四. Exchanger
用於實現兩個人之間的數據交換, 每一個人在完畢一定的事務後想與對方交換數據, 第一個先拿出數據的人將一直等待第二個人拿著數據到來時, 才幹彼此交換數據:
public class ExchangerTest { public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); final Exchanger exchanger = new Exchanger(); service.execute(new Runnable() { @Override public void run() { try { String data1 = "aaa"; System.out.println("線程 " + Thread.currentThread().getName() + " 正在把數據: " + data1 + " 換出去!"); Thread.sleep((long) Math.random() * 10000); String data2 = (String) exchanger.exchange(data1); System.out.println("線程 " + Thread.currentThread().getName() + " 換回的數據為:" + data2); } catch (Exception e) { } } }); service.execute(new Runnable() { @Override public void run() { try { String data1 = "bbb"; System.out.println("線程 " + Thread.currentThread().getName() + " 正在把數據: " + data1 + " 換出去!"); Thread.sleep((long) Math.random() * 10000); String data2 = (String) exchanger.exchange(data1); System.out.println("線程 " + Thread.currentThread().getName() + " 換回的數據為:" + data2); } catch (Exception e) { } } }); } }
線程 pool-1-thread-1 正在把數據: aaa 換出去!線程 pool-1-thread-2 正在把數據: bbb 換出去! 線程 pool-1-thread-1 換回的數據為:bbb 線程 pool-1-thread-2 換回的數據為:aaa
JDK5新特性之線程同步工具類(三)