latch java_浅谈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

假设有以下的场景,3个学生做试卷考试,老师要等所有学生做完试卷后才收卷。具体代码如下:

先看学生Student.java:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView Code

1 importjava.util.Random;2 importjava.util.concurrent.CountDownLatch;3 importjava.util.concurrent.TimeUnit;4

5 public class Student implementsRunnable {6

7 private intnum;8 privateCountDownLatch cdlatch;9

10 Student(intnum,CountDownLatch latch){11 this.num =num;12 this.cdlatch =latch;13 }14

15 @Override16 public voidrun() {17 doExam();18 try{19 TimeUnit.SECONDS.sleep(new Random().nextInt(10));20 } catch(InterruptedException e) {21 //TODO Auto-generated catch block

22 e.printStackTrace();23 }24 System.out.println("Student "+num+" finished!");25 cdlatch.countDown();26 }27

28 private voiddoExam(){29 System.out.println("Student "+num+" is doing the exam!");30 }31

32 }

再看老师Teacher.java:

1 importjava.util.concurrent.CountDownLatch;2

3 public class Teacher implementsRunnable{4

5 privateCountDownLatch cdlatch;6

7 Teacher(CountDownLatch latch){8 this.cdlatch =latch;9 }10

11 @Override12 public voidrun() {13 //TODO Auto-generated method stub

14 try{15 System.out.println("teacher is waiting...");16 cdlatch.await();17 System.out.println("teacher is collecting......");18 } catch(InterruptedException e) {19 //TODO Auto-generated catch block

20 e.printStackTrace();21 }22 }23

24 }

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.pngView Code

1 importjava.util.concurrent.CountDownLatch;2 importjava.util.concurrent.ExecutorService;3 importjava.util.concurrent.Executors;4

5 public classTestCountDownLatch {6

7 public static voidmain(String[] args) {8

9 ExecutorService executor =Executors.newCachedThreadPool();10

11 CountDownLatch latch = new CountDownLatch(3);12

13 Student s1 = new Student(101, latch);14 Student s2 = new Student(102, latch);15 Student s3 = new Student(103, latch);16 Teacher t = newTeacher(latch);17

18 executor.execute(t);19 executor.execute(s1);20 executor.execute(s2);21 executor.execute(s3);22

23 executor.shutdown();24

25 }26

27 }

我们可以看到运行的结果:

teacher is waiting...

Student 101 is doing the exam!

Student 102 is doing the exam!

Student 103 is doing the exam!

Student 102 finished!

Student 101 finished!

Student 103 finished!

teacher is collecting......

再来看一个稍微复杂点的例子,10个选手比赛跑步,在枪响后同时起跑,全部到达终点后比赛结束:

1 importjava.util.concurrent.CountDownLatch;2 importjava.util.concurrent.ExecutorService;3 importjava.util.concurrent.Executors;4

5

6 public classCountDownLatchDemo {7

8 private static int PLAYER_NUM = 10;9

10 public static voidmain(String[] args) {11

12 final CountDownLatch beginSignal = new CountDownLatch(1);13 final CountDownLatch endSignal = newCountDownLatch(PLAYER_NUM);14

15 ExecutorService executorService =Executors.newFixedThreadPool(PLAYER_NUM);16

17 for(int i=0;i

21 @Override22 public voidrun() {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次的结果:

fd4189d25b92f0eaf2ac718f69aae75c.png

可以看到,因为有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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值