CountDownLatch简笔

前言

JDK的并发包java.util.concurrent中提供了并发编程&多线程编程非常有用的工具类。本文基于CountDownLatch的源代码,介绍该类的使用及原理。

CountDownLatch介绍

CountDownLatch也叫做闭锁,根据其英文名称,可以理解为数量递减门栓。就是说门栓有个标记量,该标记量为0时门栓打开,线程可以继续执行;该标记大于0时门栓是关闭状态,所有线程等待。
官方解释是:一个同步工具,该同步工具允许一个或者多个线程等待一系列的操作被其他线程执行完成。
该类有如下方法:#await();#await(long TimeUnit);#countDown();
CountDownLatch类初始化时给定一个正整数计数器count,代表改锁要控制的线程数。调用await方法阻塞当前线程,直到调用countDown(调用该方法,计数器count值减1)方法将内部count减为0时,所有等待的线程被唤醒执行。同时接下来调用await方法会立即返回。这里有一个点要注意:计数器count的值不能被重置,如果需要重置,考虑使用CyclicBarrier类。
CountDownLatch作为同步工具功能丰富。其被初始化时传入一个计数器count作为简便门栓或大门的开关:所有线程调用CountDownLatch的await方法等待大门的打开(线程被阻塞),直到有线程调用其countDown方法。CoundDownLatch初始化时计数器为N,可表示一个线程等待N个线程完成某些操作或者某些操作被做了N次。
CountDownLatch一个有用的特性是它不需要线程在处理之前就调用countDown来等待计数器count的值减为0,它只是在所有线程都可以继续执行时通过调用await方法阻止其他线程继续执行。

使用场景

1、等待问题
一组工作线程中的两个类来使用两个闭锁。第一个CountDownLatch作为一个启动信号量,只有等Driver准备好了某些操作才能让所有Worker继续处理,否则所有Worker只能一直等待。第二个CountDownLatch作为一个结束信号量,让所有Workder都处理完成后Driver才能继续处理,否则就一直等待。归结为一句话就是启动信号量就是N个线程等待一个线程处理完,结束信号量就是一个线程等待N个线程处理完。

  class Driver { 
    void main() throws InterruptedException {
      CountDownLatch startSignal = new CountDownLatch(1);
      CountDownLatch doneSignal = new CountDownLatch(N);
      for (int i = 0; i < N; ++i) 
        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() { ... }
  }}

可以看到Driver的main函数启动了Worker的线程后,立即调用开始信号量startSignal的await方法,等待Driver的主线程的doSomethingElse()方法做完某些处理后调用startSignal的countDown方法;此时的这个场景就是N个线程等待一个线程。Driver中main方法继续调用doSomethingElse()方法,然后调用结束信号量doneSignal的await方法,等待Worker线程的调用doneSignal的countDown方法,Worker中的线程被唤醒后,执行doWork方法,然后执行doneSignal的countDown方法,最终doneSignal的count置位0后唤醒Driver的主线程。此时的场景就是一个线程等待N个线程。

2、分而治之
CountDownLatch另一个典型的用法是将一个问题切分成N部分,用Runnable来描述每一部分并执行每一部分,同时减少门栓数量(countDown方法),将这些Runnable加入到Executor中。当所有的Runnable执行完成后等待线程才允许通过继续执行(当线程必须以这种方式重复减少门栓数量即调用countDown方法时,考虑使用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() { ... }
  }

内存一致性

由于CountDownLatch是针对多线程流程控制的,所以在一个线程中调用countDown方法之前的操作(A)与在另外的线程中调用相应的await方法返回后的操作(B)符合happen-before关系。前者总是发生在后者之前,也就是A总是发生于B之前。

源码分析

CountDownLatch包含内部静态类Sync,该类继承了类AbstractQueuedSynchronizer(简称AQS),使用AQS的state来代表
CountDownLatch的count。
CountDownLatch包含的方法有countDown(),await(),await(long timeout, TimeUnit unit),getCount()。

  • AbstractQueuedSynchronizer
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容