原創文章,同步發自做者我的博客,轉載請在文章開頭處以超連接註明出處 http://www.jasongj.com/java/thread_communication/java
Java多線程編程中常常會碰到這樣一種場景——某個線程須要等待一個或多個線程操做結束(或達到某種狀態)纔開始執行。好比開發一個關發測試工具時,主線程須要等到全部測試線程均執行完成再開始統計總共耗費的時間,此時能夠經過CountDownLatch輕鬆實現。編程
package com.test.thread; import java.util.Date; import java.util.concurrent.CountDownLatch; public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { int totalThread = 3; long start = System.currentTimeMillis(); CountDownLatch countDown = new CountDownLatch(totalThread); for(int i = 0; i < totalThread; i++) { final String threadName = "Thread " + i; new Thread(() -> { System.out.println(String.format("%s\t%s %s", new Date(), threadName, "started")); try { Thread.sleep(1000); } catch (Exception ex) { ex.printStackTrace(); } countDown.countDown(); System.out.println(String.format("%s\t%s %s", new Date(), threadName, "ended")); }).start();; } countDown.await(); long stop = System.currentTimeMillis(); System.out.println(String.format("Total time : %sms", (stop - start))); } }
執行結果安全
Sun Jun 19 20:34:31 CST 2016 Thread 1 started Sun Jun 19 20:34:31 CST 2016 Thread 0 started Sun Jun 19 20:34:31 CST 2016 Thread 2 started Sun Jun 19 20:34:32 CST 2016 Thread 2 ended Sun Jun 19 20:34:32 CST 2016 Thread 1 ended Sun Jun 19 20:34:32 CST 2016 Thread 0 ended Total time : 1072ms
能夠看到,主線程等待全部3個線程都執行結束後纔開始執行。多線程
CountDownLatch工做原理相對簡單,能夠簡單當作一個倒計時器,在構造方法中指定初始值,每次調用countDown()方法時講計數器減1,而await()會等待計數器變爲0。CountDownLatch關鍵接口以下併發
在《當咱們說線程安全時,到底在說什麼》一文中講過內存屏障,它能保證屏障以前的代碼必定在屏障以後的代碼以前被執行。CyclicBarrier能夠譯爲循環屏障,也有相似的功能。CyclicBarrier能夠在構造時指定須要在屏障前執行await的個數,全部對await的調用都會等待,只到調用await的次數達到預約指,全部等待都會當即被喚醒。ide
從使用場景上來講,CyclicBarrier是讓多個線程互相等待某一事件的發生,而後同時被喚醒。而上文講的CountDownLatch是讓某一線程等待多個線程的狀態,而後該線程被喚醒。工具
package com.test.thread; import java.util.Date; import java.util.concurrent.CyclicBarrier; public class CyclicBarrierDemo { public static void main(String[] args) { int totalThread = 5; CyclicBarrier barrier = new CyclicBarrier(totalThread); for(int i = 0; i < totalThread; i++) { String threadName = "Thread " + i; new Thread(() -> { System.out.println(String.format("%s\t%s %s", new Date(), threadName, " is waiting")); try { barrier.await(); } catch (Exception ex) { ex.printStackTrace(); } System.out.println(String.format("%s\t%s %s", new Date(), threadName, "ended")); }).start(); } } }
執行結果以下測試
Sun Jun 19 21:04:49 CST 2016 Thread 1 is waiting Sun Jun 19 21:04:49 CST 2016 Thread 0 is waiting Sun Jun 19 21:04:49 CST 2016 Thread 3 is waiting Sun Jun 19 21:04:49 CST 2016 Thread 2 is waiting Sun Jun 19 21:04:49 CST 2016 Thread 4 is waiting Sun Jun 19 21:04:49 CST 2016 Thread 4 ended Sun Jun 19 21:04:49 CST 2016 Thread 0 ended Sun Jun 19 21:04:49 CST 2016 Thread 2 ended Sun Jun 19 21:04:49 CST 2016 Thread 1 ended Sun Jun 19 21:04:49 CST 2016 Thread 3 ended
從執行結果能夠看到,每一個線程都不會在其它全部線程執行await()方法前繼續執行,而等全部線程都執行await()方法後全部線程的等待都被喚醒從而繼續執行。線程
CyclicBarrier提供的關鍵方法以下調試
CountDownLatch和CyclicBarrier都是JDK 1.5引入的,而Phaser是JDK 1.7引入的。Phaser的功能與r
CountDownLatch和CyclicBarrier有部分重疊,同時也提供了更豐富的語義和更靈活的用法。
Phaser顧名思義,與階段相關。Phaser比較適合這樣一種場景,一種任務能夠分爲多個階段,現但願多個線程去處理該批任務,對於每一個階段,多個線程能夠併發進行,可是但願保證只有前面一個階段的任務完成以後才能開始後面的任務。這種場景可使用多個CyclicBarrier來實現,每一個CyclicBarrier負責等待一個階段的任務所有完成。可是使用CyclicBarrier的缺點在於,須要明確知道總共有多少個階段,同時並行的任務數須要提早預約義好,且沒法動態修改。而Phaser可同時解決這兩個問題。
public class PhaserDemo { public static void main(String[] args) throws IOException { int parties = 3; int phases = 4; final Phaser phaser = new Phaser(parties) { @Override protected boolean onAdvance(int phase, int registeredParties) { System.out.println("====== Phase : " + phase + " ======"); return registeredParties == 0; } }; for(int i = 0; i < parties; i++) { int threadId = i; Thread thread = new Thread(() -> { for(int phase = 0; phase < phases; phase++) { System.out.println(String.format("Thread %s, phase %s", threadId, phase)); phaser.arriveAndAwaitAdvance(); } }); thread.start(); } } }
執行結果以下
Thread 0, phase 0 Thread 1, phase 0 Thread 2, phase 0 ====== Phase : 0 ====== Thread 2, phase 1 Thread 0, phase 1 Thread 1, phase 1 ====== Phase : 1 ====== Thread 1, phase 2 Thread 2, phase 2 Thread 0, phase 2 ====== Phase : 2 ====== Thread 0, phase 3 Thread 1, phase 3 Thread 2, phase 3 ====== Phase : 3 ======
從上面的結果能夠看到,多個線程必須等到其它線程的同一階段的任務所有完成才能進行到下一個階段,而且每當完成某一階段任務時,Phaser都會執行其onAdvance方法。
Phaser主要接口以下