CyclicBarrier及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:

 1 import java.util.Random;
 2 import java.util.concurrent.CountDownLatch;
 3 import java.util.concurrent.TimeUnit;
 4
 5 public class Student implements Runnable {
 6
 7     private int num;
 8     private CountDownLatch cdlatch;
 9
10     Student(int num,CountDownLatch latch){
11         this.num = num;
12         this.cdlatch = latch;
13     }
14
15     @Override
16     public void run() {
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 void doExam(){
29         System.out.println("Student "+num+" is doing the exam!");
30     }
31
32 }

再看老师Teacher.java:

 1 import java.util.concurrent.CountDownLatch;
 2
 3 public class Teacher implements Runnable{
 4
 5     private CountDownLatch cdlatch;
 6
 7     Teacher(CountDownLatch latch){
 8         this.cdlatch = latch;
 9     }
10
11     @Override
12     public void run() {
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 }

 1 import java.util.concurrent.CountDownLatch;
 2 import java.util.concurrent.ExecutorService;
 3 import java.util.concurrent.Executors;
 4
 5 public class TestCountDownLatch {
 6
 7     public static void main(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 = new Teacher(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 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之后了。

参考:

http://www.cnblogs.com/shiyanch/archive/2011/04/04/2005233.html

http://blackgu.blogbus.com/logs/69596661.html

CyclicBarrier和CountDownLatch一样,都是关于线程的计数器。

用法略有不同,测试代码如下:

 1 public class TestCyclicBarrier {
 2
 3     private static final int THREAD_NUM = 5;
 4
 5     public static class WorkerThread implements Runnable{
 6
 7         CyclicBarrier barrier;
 8
 9         public WorkerThread(CyclicBarrier b){
10             this.barrier = b;
11         }
12
13         @Override
14         public void run() {
15             // TODO Auto-generated method stub
16             try{
17                 System.out.println("Worker‘s waiting");
18                 //线程在这里等待,直到所有线程都到达barrier。
19                 barrier.await();
20                 System.out.println("ID:"+Thread.currentThread().getId()+" Working");
21             }catch(Exception e){
22                 e.printStackTrace();
23             }
24         }
25
26     }
27
28     /**
29      * @param args
30      */
31     public static void main(String[] args) {
32         // TODO Auto-generated method stub
33         CyclicBarrier cb = new CyclicBarrier(THREAD_NUM, new Runnable() {
34             //当所有线程到达barrier时执行
35             @Override
36             public void run() {
37                 // TODO Auto-generated method stub
38                 System.out.println("Inside Barrier");
39
40             }
41         });
42
43         for(int i=0;i<THREAD_NUM;i++){
44             new Thread(new WorkerThread(cb)).start();
45         }
46     }
47
48 }
49 /*
50 以下是输出:
51 Worker‘s waiting
52 Worker‘s waiting
53 Worker‘s waiting
54 Worker‘s waiting
55 Worker‘s waiting
56 Inside Barrier
57 ID:12 Working
58 ID:8 Working
59 ID:11 Working
60 ID:9 Working
61 ID:10 Working
62 */

  1. CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续。
  2. CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。
  3. CyclicBarrier初始时还可带一个Runnable的参数, 此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。
时间: 2024-11-03 22:08:41

CyclicBarrier及CountDownLatch的使用的相关文章

CyclicBarrier和CountDownLatch的差别

CyclicBarrier和CountDownLatch都用多个线程之间的同步,共同点:同时有N个线程在 CyclicBarrier(CountDownLatch) 等待上等待时,CyclicBarrier(CountDownLatch) 的wait 函数满足条件,会唤醒等待线程,从 等待线程的wait()出返回. 不同点:CountDownLatch latch=new CountDownLatch(2); thread1:latch.wait() .不满足 ,线程切换到等待状态thread2

《java.util.concurrent 包源码阅读》21 CyclicBarrier和CountDownLatch

CyclicBarrier是一个用于线程同步的辅助类,它允许一组线程等待彼此,直到所有线程都到达集合点,然后执行某个设定的任务. 现实中有个很好的例子来形容:几个人约定了某个地方集中,然后一起出发去旅行.每个参与的人就是一个线程,CyclicBarrier就是那个集合点,所有人到了之后,就一起出发. CyclicBarrier的构造函数有两个: // parties是参与等待的线程的数量,barrierAction是所有线程达到集合点之后要做的动作 public CyclicBarrier(in

JAVA多线程提高十:同步工具CyclicBarrier与CountDownLatch

今天继续学习其它的同步工具:CyclicBarrier与CountDownLatch 一.CyclicBarrier CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用.因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier. CyclicBarrier类似于CountDownL

CyclicBarrier和CountDownLatch的区别

 CountDownLatch Countdownlatch是一个同步工具类:用来协调多个线程之间的同步: 这个工具通常用来控制线程等待:它可以让某一个线程等待知道倒计时结束,在开始执行: CountDownLatch的两种用法: 1. 某一线程在开始运行前等待n个线程执行完毕:将CountDownLatch的计数器初始化为n:new CountDownLatch(n);每当一个任务线程执行完毕,就将计数器减1,CountDownLatch.Countdown:当计数器的值变为0时:在Count

CyclicBarrier与CountDownLatch的区别

import java.util.concurrent.CountDownLatch; /** * 作用于单个线程或几个线程,,在其他线程执行完之前,一直等待(await)知道countDown为零 * @author Administrator * */ public class CountDownlatchTest { public static void main(String[] args) throws InterruptedException { CountDownLatch cou

CyclicBarrier、CountDownLatch与Semaphore的小记

CyclicBarrier: 适合的业务场景,比如 1).,现有一大任务,需要得到全年的统计数据的,这个工作量是巨大的,那么可以将其分割为12个月的子任务,各个子任务相互独立,当所有子任务完成了,则就可以进行全年统计了,这样大大提升了统计效率. 2).大家一起去郊游,由于大家住的地方比较分散,故需要一个集合点之后一起出发,这样大家才能玩得开心嘛. ....等等 就是当有一个大任务时,需要分配多个子任务去执行,只有当所有的子任务都执行完成后,才能执行主任务. CountDownLatch: 适合的

练习题之CyclicBarrier与CountDownLatch

一.CyclicBarrier使用 new Thread().start(); new Thread(new Runnable() {},"XXX").start(); public class CyclicBarrierTest { public static int NUM=10; public static void main(String[] args) { CyclicBarrier cyc = new CyclicBarrier(10); for(int i=0;i<

Java 并发同步器之CountDownLatch、CyclicBarrier

一.简介 1.CountDownLatch是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0 时,await()方法会阻塞程序的执行. CountDownLatch可以看作是一个倒计数的锁存器,当计数减至0时触发特定的事件.利用这种特性,可以让主线程等待子线程的结束. CountDownLatch最重要的方法是countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待

Join,CountDownLatch,CyclicBarrier,Semaphore和Exchanger

CountDownLatch允许一个或者多个线程等待其他线程完成操作,之后再对结果做统一处理: 适用场景,分布式系统中对多个微服务的调用,并发执行并且必须等待全部执行完成才能继续执行后续操作: 其实在java中默认的实现是join()方法,join()方法主要的作用是当前线程必须等待直到join线程执行完成之后才能继续执行后续的操作, 其本质就是轮询判断join线程是否存活,如果存活则主线程继续等待,否则,通过调用this.notifyAll()方法来继续执行主线程. 实例代码如下: publi