浅谈Java中CountDownLatch的用法

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?


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值