在網上看到很多人對於CountDownLatch和CyclicBarrier的區別簡單理解為CountDownLatch是一次性的,而 CyclicBarrier在調用reset之后還可以繼續使用。那如果只是這么簡單的話,我覺得CyclicBarrier簡單命名為ResetableCountDownLatch好了,顯然不是的。
我的理解是,要從他們的設計目的去看這兩個類。javadoc里面的描述是這樣的。
CountDownLatch: A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
CyclicBarrier : A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.
可能是我的英語不夠好吧, 我感覺從這個javadoc里面要准確理解他們的差異還是不容易的。
我的理解是
CountDownLatch : 一個線程(或者多個), 等待另外N個線程完成某個事情之后才能執行。 CyclicBarrier : N個線程相互等待,任何一個線程完成之前,所有的線程都必須等待。
這樣應該就清楚一點了,對於CountDownLatch來說,重點是那個“一個線程”, 是它在等待, 而另外那N的線程在把“某個事情”做完之后可以繼續等待,可以終止。而對於CyclicBarrier來說,重點是那N個線程,他們之間任何一個沒有完成,所有的線程都必須等待。
CountDownLatch 是計數器, 線程完成一個就記一個, 就像 報數一樣, 只不過是遞減的.
而CyclicBarrier更像一個水閘, 線程執行就想水流, 在水閘處都會堵住, 等到水滿(線程到齊)了, 才開始泄流.
CyclicBarrier
假設有只有的一個場景:每個線程代表一個跑步運動員,當運動員都准備好后,才一起出發,只要有一個人沒有准備好,大家都等待.
import java.io.IOException; import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class Runner implements Runnable { private CyclicBarrier barrier; private String name; public Runner(CyclicBarrier barrier, String name) { super(); this.barrier = barrier; this.name = name; } @Override public void run() { try { Thread.sleep(1000 * (new Random()).nextInt(8)); System.out.println(name + " 准備OK."); barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(name + " Go!!"); } } public class Race { public static void main(String[] args) throws IOException, InterruptedException { CyclicBarrier barrier = new CyclicBarrier(3); ExecutorService executor = Executors.newFixedThreadPool(3); executor.submit(new Thread(new Runner(barrier, "zhangsan"))); executor.submit(new Thread(new Runner(barrier, "lisi"))); executor.submit(new Thread(new Runner(barrier, "wangwu"))); executor.shutdown(); } }
總結:CyclicBarrier就是一個柵欄,等待所有線程到達后再執行相關的操作。barrier 在釋放等待線程后可以重用。
CountDownLatch
import java.util.concurrent.CountDownLatch; /** * 示例:CountDownLatch的使用舉例 * Mail: ken@iamcoding.com * @author janeky */ public class TestCountDownLatch { private static final int N = 10; public static void main(String[] args) throws InterruptedException { CountDownLatch doneSignal = new CountDownLatch(N); CountDownLatch startSignal = new CountDownLatch(1);//開始執行信號 for (int i = 1; i <= N; i++) { new Thread(new Worker(i, doneSignal, startSignal)).start();//線程啟動了 } System.out.println("begin------------"); startSignal.countDown();//開始執行啦 doneSignal.await();//等待所有的線程執行完畢 System.out.println("Ok"); } static class Worker implements Runnable { private final CountDownLatch doneSignal; private final CountDownLatch startSignal; private int beginIndex; Worker(int beginIndex, CountDownLatch doneSignal, CountDownLatch startSignal) { this.startSignal = startSignal; this.beginIndex = beginIndex; this.doneSignal = doneSignal; } public void run() { try { startSignal.await(); //等待開始執行信號的發布 beginIndex = (beginIndex - 1) * 10 + 1; for (int i = beginIndex; i <= beginIndex + 10; i++) { System.out.println(i); } } catch (InterruptedException e) { e.printStackTrace(); } finally { doneSignal.countDown(); } } } }
總結:CounDownLatch對於管理一組相關線程非常有用。上述示例代碼中就形象地描述了兩種使用情況。第一種是計算器為1,代表了兩種狀態,開 關。第二種是計數器為N,代表等待N個操作完成。今后我們在編寫多線程程序時,可以使用這個構件來管理一組獨立線程的執行。