zoukankan      html  css  js  c++  java
  • Java并发编程--CountDownLatch

    概述

      CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。

      CountDownLatch使用一个计数器count实现,构建CountDownLatch时需要使用给定的count初始化CountDownLatch。在count到达0之前,调用await()方法的线程将一直阻塞,当count到达0时,会唤醒所有阻塞的线程。注意:计数器count无法被重置,即只能实现一次这种功能,这也是CountDownLatch与CyclicBarrier的区别。

      内存一致性效果:线程中调用 countDown() 之前的操作 happen-before 紧跟在从另一个线程中对应 await() 成功返回的操作。 

    使用

      CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。

      提供的方法:

    1 //使当前线程阻塞直到计数器count变为0,除非被中断
    2 public void await() throws InterruptedException
    3 //使当前线程阻塞直到计数器count变为0,除非被中断或超过了指定时间
    4 public boolean await(long timeout, TimeUnit unit) throws InterruptedException
    5 //将计数器count递减,若count变为0则唤醒所有等待的线程
    6 public void countDown()
    7 //返回计数器count值
    8 public long getCount()

      使用示例:Driver主线程中控制N个Worker线程的启动,并等待所有Worker线程完成再退出。

     1 class Driver { // ...
     2     void main() throws InterruptedException {
     3         CountDownLatch startSignal = new CountDownLatch(1);    //启动信号
     4         CountDownLatch doneSignal = new CountDownLatch(N);    //完成信号
     5 
     6         for (int i = 0; i < N; ++i) // create and start threads
     7             new Thread(new Worker(startSignal, doneSignal)).start();
     8 
     9         doSomethingElse();            // don't let run yet
    10         //将启动信号的计数器置为0,启动等待的Worker线程
    11         startSignal.countDown();      // let all threads proceed
    12         doSomethingElse();
    13         //等待N个Worker线程完成
    14         doneSignal.await();           // wait for all to finish
    15     }
    16 }
    17 
    18 class Worker implements Runnable {
    19     private final CountDownLatch startSignal;
    20     private final CountDownLatch doneSignal;
    21     Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
    22         this.startSignal = startSignal;
    23         this.doneSignal = doneSignal;
    24     }
    25     public void run() {
    26         try {
    27             startSignal.await();    //等待启动信号
    28             doWork();
    29             doneSignal.countDown();    //发出完成信号,将完成信号的计数器减1
    30         } catch (InterruptedException ex) {} // return;
    31     }
    32 
    33     void doWork() { ... }
    34 }

    实现原理

      CountDownLatch基于AQS实现,使用AQS的同步状态state表示计数器count。

      先看一下CountDownLatch的内部类Syns的实现:

     1 private static final class Sync extends AbstractQueuedSynchronizer {
     2     private static final long serialVersionUID = 4982264981922014374L;
     3 
     4     Sync(int count) {
     5         setState(count);    //设置同步状态state为count
     6     }
     7 
     8     int getCount() {
     9         return getState();    //查询同步状态
    10     }
    11     //重写AQS的共享式获取同步状态的方法。当state=0时返回1,获取成功;当state=1时返回-1,获取失败。
    12     protected int tryAcquireShared(int acquires) {
    13         return (getState() == 0) ? 1 : -1;
    14     }
    15     //重写AQS的共享式释放同步状态的方法。基于自旋CAS递减同步状态
    16     protected boolean tryReleaseShared(int releases) {
    17         // Decrement count; signal when transition to zero
    18         //如果state=0,那么直接返回false
    19         //如果state>0,那么递减state。若更新后的state=0则返回true,释放同步状态成功;反之,返回false。
    20         for (;;) {
    21             int c = getState();
    22             if (c == 0)
    23                 return false;
    24             int nextc = c-1;
    25             if (compareAndSetState(c, nextc))
    26                 return nextc == 0;
    27         }
    28     }
    29 }

        由Sync源码可以看出,CountDownLatch基于AQS的共享式获取和释放同步状态的机制实现。

      await()

    1 //调用AQS提供的共享式可中断获取同步状态方法。
    2 //若获取成功(state=0),继续执行后续代码;否则(state>0),阻塞当前线程。
    3 public void await() throws InterruptedException {
    4     sync.acquireSharedInterruptibly(1);    
    5 }

      countDown()

    1 //调用AQS提供的共享式释放同步状态方法。
    2 //若释放成功(tryReleaseShared返回true),唤醒同步队列上的后继节点;若释放失败(tryReleaseShared返回false),不做任何操作。
    3 public void countDown() {
    4     sync.releaseShared(1);
    5 }
  • 相关阅读:
    [JXOI2018]游戏
    UltraISO制作启动盘安装CentOS7
    龙芯、鲲鹏、飞腾等国产平台的浏览器
    浏览器相关性能测试网址
    有没有一种组合字体,中文是宋体,英文是times new roman?
    中国航天科工集团第二研究院706所招聘
    华为云
    面试阿里P6难在哪?(面试难点)
    Linux下配置环境变量—— .bashrc 和 /etc/profile
    Linux 内存 占用较高问题排查
  • 原文地址:https://www.cnblogs.com/zaizhoumo/p/7786893.html
Copyright © 2011-2022 走看看