CountDownLatch位于java.util.concurrent包下,是JDK1.5的并发包下的新特性。
首先根据Oracle的官方文档看看CountDownLatch的定义:
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
简单来说,CountDownLatch是一个同步的辅助类,允许一个或多个线程一直等待,直到其它线程完成它们的操作。
这里就涉及两个问题:
1.如何让一个或多个线程一直等待;
2.如何让这些线程知道其它线程已经完成它们的操作
这两个问题主要是使用一个count的属性解决。使用count初始化CountDownLatch,然后需要等待的线程调用await方法。await
方法会一直受阻塞直到count=0。
而其它线程完成自己的操作后,调用countDown()使计数器count减1。当count减到0时,所有在等待的线程均会被释放,并且count无法被重置。如果需要重置,请参考
CyclicBarrier
再来看一个稍微复杂点的例子,10个选手比赛跑步,在枪响后同时起跑,全部到达终点后比赛结束:
1 import java.util.concurrent.CountDownLatch; 2 import java.util.concurrent.ExecutorService; 3 import java.util.concurrent.Executors; 4 5 6 public class CountDownLatchDemo { 7 8 private static int PLAYER_NUM = 10; 9 10 public static void main(String[] args) { 11 12 final CountDownLatch beginSignal = new CountDownLatch(1); 13 final CountDownLatch endSignal = new CountDownLatch(PLAYER_NUM); 14 15 ExecutorService executorService = Executors.newFixedThreadPool(PLAYER_NUM); 16 17 for(int i=0;i<PLAYER_NUM;i++){ 18 final int num = i+1; 19 Runnable runner = new Runnable(){ 20 21 @Override 22 public void run() { 23 // TODO Auto-generated method stub 24 System.out.println("No. "+num+" is waiting..."); 25 try { 26 beginSignal.await(); 27 System.out.println("No. "+num+" begin running"); 28 Thread.sleep((long) (Math.random() * 10000)); 29 System.out.println("No." + num + " arrived"); 30 } catch (InterruptedException e) { 31 // TODO Auto-generated catch block 32 e.printStackTrace(); 33 }finally{ 34 endSignal.countDown(); 35 } 36 } 37 38 }; 39 executorService.execute(runner); 40 } 41 42 System.out.println("before Game Start"); 43 beginSignal.countDown(); 44 System.out.println("Game Start"); 45 System.out.println("---In the middle of the game---"); 46 try { 47 endSignal.await(); 48 } catch (InterruptedException e) { 49 // TODO Auto-generated catch block 50 e.printStackTrace(); 51 }finally{ 52 System.out.println("Game Over!"); 53 executorService.shutdown(); 54 } 55 56 } 57 58 }
以上逻辑不难理解,beginSignal的count=0时,runner线程开始运行,直到endSignal的count=0时结束。
接下来分析一下运行6次的结果:
可以看到,因为有beginSignal,所以可以保证所有runner都waiting以后,才begin running。同理,因为有endSignal,可以保证所有runner arrived后才Game Over!
但是,这里的需要留意主线程的几个输出:
1 System.out.println("before Game Start"); 2 beginSignal.countDown(); 3 System.out.println("Game Start"); 4 System.out.println("---In the middle of the game---");
1.尽管before Game Start在countDown()之前,但不能保证is waiting全部输出完后,才输出before Game Start。
2.“Game Start”和"In the middlel of the game"虽然都在countDown()之后,但在多线程的环境下(主线程也是线程之一),无法预计两个字段输出的位置。从上面的case看,有可能在running的前面,中间和后面,无法预计。这里要十分注意。
3.因为有Thread.sleep,所以arrived都在running之后出现。否则,arrived出现的位置,就不一定都在running之后了。
对于第一点,其实还没想明白,为什么顺序是No.9 is waiting --> before Game Start --> No.10 is waiting 而不是 No.9 is waiting --> No.10 is waiting --> before Game Start?