CountDownLatch至關於一個門閂,門閂上掛了N把鎖。只有N把鎖都解開的話,門纔會打開。怎麼理解呢?我舉一個賽跑比賽的例子,賽跑比賽中必須等待全部選手都準備好了,裁判才能開發令槍。選手才能夠開始跑。CountDownLatch當中主要有兩個方法,一個是await()會掛上鎖阻塞當前線程,至關於裁判站在起始點等待,等待各位選手準備就緒,一個是countDown方法用於解鎖,至關於選手準備好了以後調用countDown方法告訴裁判本身準備就緒,當全部人都準備好了以後裁判開發令槍。多線程
public class TestCountDownLatch { public static void main(String[] args) { // 須要等待兩個線程,因此傳入參數爲2 CountDownLatch latch = new CountDownLatch(2); // 該線程運行1秒 new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("1號選手準備就緒!用時1秒!"); latch.countDown(); } }).start(); // 該線程運行3秒 new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("2號選手準備就緒!用時3秒!"); latch.countDown(); } }).start(); try { System.out.println("請1號選手和2號選手各就各位!"); // 主線程在此等待兩個線程執行完畢以後繼續執行 latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } // 兩個線程執行完畢後,主線程恢復運行 System.out.println("裁判發槍,1號選手和2號選手開跑!"); } }
請1號選手和2號選手各就各位! 1號選手準備就緒!用時1秒! 2號選手準備就緒!用時3秒! 裁判發槍,1號選手和2號選手開跑!
若是去掉CountDownLatch的效果呢?運行結果就會變成以下:ide
請1號選手和2號選手各就各位! 裁判發槍,1號選手和2號選手開跑! 1號選手準備就緒!用時1秒! 2號選手準備就緒!用時3秒!
裁判就會在選手還未準備就緒的時候開發令槍,這就亂套了。
其實CountDownLatch一個最簡單的用處就是計算多線程執行完畢時的時間。像剛纔的例子當中兩個線程並行執行了共花費了3秒鐘。工具
CyclicBarrier就像一個柵欄,將各個線程攔住。Cyclic是循環的英文,代表該工具能夠進行循環使用。CyclicBarrier(N)的構造參數代表該一共有幾個線程須要互相等待。它至關於N個選手約定進行屢次比賽,每次比賽完都要在起跑點互相等待。讀者可能會立刻疑惑這不是和CountDownLatch同樣嗎?不同。由於CountDownLatch是裁判等待選手,是調用await()方法的線程,等待調用countDown()方法的各個線程。而CyclicBarrier是選手等待選手,是調用await()方法的線程互相等待,等待其餘線程都運行好以後,再開始下一輪運行。 ui
咱們舉一個例子,兩個選手進行比賽,一共進行三輪比賽。線程
public class TestCyclicBarrier { // 1號選手跑的輪數 public static int countA = 1; // 2號選手跑的輪數 public static int countB = 1; public static void main(String[] args) { // 填入2,表明2個線程互相等待 CyclicBarrier barrier = new CyclicBarrier(2); new Thread(new Runnable() { @Override public void run() { // 一共跑三輪 for (int i = 0; i < 3; i++) { System.out.println("1號選手開始跑!當前第" + countA++ + "輪比賽!"); // 1號選手跑得慢,每次跑三秒 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } try { System.out.println("1號選手抵達終點!"); // 調用等待方法,在此等待其餘選手 barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { // 一共等待三輪 for (int i = 0; i < 3; i++) { System.out.println("2號選手開始跑!當前第" + countB++ + "輪比賽!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } try { System.out.println("2號選手抵達終點!"); // 調用等待方法,在此等待其餘選手 barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } } }).start(); } }
1號選手開始跑!當前第1輪比賽! 2號選手開始跑!當前第1輪比賽! 2號選手抵達終點! 1號選手抵達終點! 1號選手開始跑!當前第2輪比賽! 2號選手開始跑!當前第2輪比賽! 2號選手抵達終點! 1號選手抵達終點! 1號選手開始跑!當前第3輪比賽! 2號選手開始跑!當前第3輪比賽! 2號選手抵達終點! 1號選手抵達終點!
每輪比賽1號選手和2號選手都會回到起跑線互相等待,再開啓下一輪比賽。
若是不加CyclicBarrier呢?code
1號選手開始跑!當前第1輪比賽! 2號選手開始跑!當前第1輪比賽! 2號選手抵達終點! 2號選手開始跑!當前第2輪比賽! 2號選手抵達終點! 2號選手開始跑!當前第3輪比賽! 1號選手抵達終點! 1號選手開始跑!當前第2輪比賽! 2號選手抵達終點! 1號選手抵達終點! 1號選手開始跑!當前第3輪比賽! 1號選手抵達終點!
此時2號選手就直接跑完三輪比賽,不等1號選手了。遊戲
Semaphore英文的字面意思是信號量。它的工做機制是每一個線程想要獲取運行的機會的話,都必須獲取到信號量。acquire()方法阻塞的獲取信號量,release()釋放信號量。舉個例子,假設咱們去迪士尼遊玩,可是迪士尼擔憂遊客不少的話,影響你們的遊玩體驗,因而規定每一個小時只能賣出兩張門票。這樣就能夠控制在遊樂園當中的遊客數量了。開發
public class TestSemaphore { public static void main(String[] args) { Semaphore semaphore = new Semaphore(0); System.out.println("顧客在售票處等候中"); new Thread(new Runnable() { @Override public void run() { for (; ; ) { try { Thread.sleep(500); // 等待出票 semaphore.acquire(); System.out.println("顧客拿到門票入場!"); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 3; i++) { try { // 等待一小時再發門票 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // 一次性發出兩張門票 System.out.println("售票處第" + (i + 1) + "小時售出兩張票!"); semaphore.release(); semaphore.release(); } } }).start(); System.out.println("售票處開始售票!"); } }
顧客在售票處等候中... 售票處開始售票! 售票處第1小時售出兩張票! 顧客拿到門票入場! 顧客拿到門票入場! 售票處第2小時售出兩張票! 顧客拿到門票入場! 顧客拿到門票入場! 售票處第3小時售出兩張票! 顧客拿到門票入場! 顧客拿到門票入場!
Exchanger提供了讓兩個線程互相交換數據的同步點。Exchanger有點像2個線程的CyclicBarrier,線程之間都是互相等待,區別在於Exchanger多了交換的操做。舉個例子比如之前玩網遊的時候,買家和賣家必須走到地圖上同一地點面對面進行交易同樣,一手交錢一手交裝備。get
public class TestExchanger { public static void main(String[] args) { Exchanger<String> exchanger = new Exchanger<>(); new Thread(new Runnable() { @Override public void run() { String weapon = "裝備"; System.out.println("我是賣家,我帶着" + weapon + "過來了!"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("賣家到達地圖上交易地點"); try { System.out.println("我是賣家,換回了" + exchanger.exchange(weapon)); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { String money = "一萬遊戲幣"; System.out.println("我是買家,我帶着" + money + "過來了"); try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("買家到達地圖上交易地點"); try { System.out.println("我是買家,換回了" + exchanger.exchange(money)); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); }
我是賣家,我帶着裝備過來了! 我是買家,我帶着一萬遊戲幣過來了 賣家達到交易地點 買家到達交易地點 我是買家,換回了裝備 我是賣家,換回了一萬遊戲幣