关闭

CountDownLatch认知——同步协调器

标签: jdk线程并发
184人阅读 评论(0) 收藏 举报
分类:

简介

java.util.concurrent.CountDownLatchJDK1.5引入的并法包中的一个工具类,用来做并发线程间的同步。

我们来看一下它的类说明信息:

一个同步辅助,它允许一个或者多个线程等待——直到一系列在其他线程中的操作完成之后才进行。

一个CountDownLatch 由一个给定的count总数初始化。await方法会一直阻塞直到当前count值被countDown方法的调用减为零,之后,所有等待线程被释放,任何后续await方法的调用立即返回。这种现象是一次性的——count值不能被重置。如果你想要count能被重置,请考虑使用CyclicBarrier

一个CountDownLatch是一个万能的同步工具,它可以被用在很多场景中。CountDownLatch类似于简单开关门闩,或者说大门:所有调用await的线程在大门外等待直到大门被一个调用countDown的线程打开。一个CountDownLatch的计数器被初始化为N,可以使一个线程等待直到N个线程完成了某些操作,或者某些操作被完成了N次。

CountDownLatch 一个有用的特性是它并不要求调用countDown的线程必须等待count值减为零之后才能进行,它只是简单地阻止任意的线程执行传递await信号直到所有线程可以传递这个信号。


协调器

所以,我所理解的这个类的最主要的作用就是:用来做线程同步的协调器
讲一个我理解的场景:

大学里的体育课,这里边有两个角色:体育老师和学生。上课开始的前15分钟,体育老师定为热身阶段,让大家先去跑个1000米,跑完的同学先去自由休息,听老师命令,到指定地点正式上课。

这个场景就非常适合用CountDownLath来做协调。
伪代码标识为:

//体育老师宣布活动
//以学生总数目作为CountDownLath的计数器的初始值
//每个学生都是一个线程,并持有CountDownLath的引用
//所有学生在起跑线预备,等待体育老师鸣哨
//体育老师鸣哨,计时开始,然后就去凉快的地方待着
//学生在听到哨声之后,开始各自奔跑
//每个学生在跑过终点之后,CountDownLath的计数器减一
//所有跑完,CountDownLath为零,老师回到操场,宣布下一个活动

示例

官方给的使用样例:这里有一对儿类,它们有一组工作线程使用两个countdown latch。

一开始的start(开始)信号,阻止任意的worker执行直到driver已经准备好执行;
第二个的completion(完成)信号,允许dirver等待直到所有worker已经完成。

 class Driver { // ...
   void main() throws InterruptedException {
     CountDownLatch startSignal = new CountDownLatch(1);
     CountDownLatch doneSignal = new CountDownLatch(N);

     for (int i = 0; i < N; ++i) // create and start threads
       new Thread(new Worker(startSignal, doneSignal)).start();

     doSomethingElse();            // don't let run yet
     startSignal.countDown();      // let all threads proceed
     doSomethingElse();
     doneSignal.await();           // wait for all to finish
   }
 }
 class Worker implements Runnable {
   private final CountDownLatch startSignal;
   private final CountDownLatch doneSignal;
   Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
     this.startSignal = startSignal;
     this.doneSignal = doneSignal;
   }
   public void run() {
     try {
       startSignal.await();
       doWork();
       doneSignal.countDown();
     } catch (InterruptedException ex) {} // return;
   }

   void doWork() { ... }
 }}

另外一个典型的应用是把一个问题分解为N个部分,每个部分用一个Runnable来描述,一个Runnable执行每个部分并削减latch,然后将所有的Runnable送到一个Executor的队列中。当所有的子部分都完成了,协调线程会传递一个await信号(当线程必须以这种方式重复削减的时候,请使用CyclicBarrier)。

 class Driver2 { // ...
   void main() throws InterruptedException {
     CountDownLatch doneSignal = new CountDownLatch(N);
     Executor e = ...

     for (int i = 0; i < N; ++i) // create and start threads
       e.execute(new WorkerRunnable(doneSignal, i));

     doneSignal.await();           // wait for all to finish
   }
 }
 class WorkerRunnable implements Runnable {
   private final CountDownLatch doneSignal;
   private final int i;
   WorkerRunnable(CountDownLatch doneSignal, int i) {
     this.doneSignal = doneSignal;
     this.i = i;
   }
   public void run() {
     try {
       doWork(i);
       doneSignal.countDown();
     } catch (InterruptedException ex) {} // return;
   }

   void doWork() { ... }
 }

内存一致性影响:直到计数器减为零,线程中countDown方法被调用之前的动作happen-before于后续的另外一个线程中相应的await()方法成功返回之后的动作。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:34500次
    • 积分:658
    • 等级:
    • 排名:千里之外
    • 原创:31篇
    • 转载:0篇
    • 译文:1篇
    • 评论:5条
    最新评论