浅谈Java中CountDownLatch的用法

来源:互联网 发布:php 敏感词过滤 编辑:程序博客网 时间:2024/04/30 07:08

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                 @Override22                 public void run() {23                     // TODO Auto-generated method stub24                     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 block32                         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 block50             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?


原创粉丝点击