19.详解AQS家族的成员:CountDownLatch

这篇具有很好参考价值的文章主要介绍了19.详解AQS家族的成员:CountDownLatch。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

关注王有志,一个分享硬核Java技术的互金摸鱼侠
欢迎你加入Java人的提桶跑路群:共同富裕的Java人

今天我们来聊一聊AQS家族中的另一个重要成员CountDownLatch。关于CountDownLatch的面试题并不多,除了问“是什么”和“如何实现的“外,CountDownLatch还会和CyclicBarrier进行对比:

  • 什么是CountDownLatch?它是如何实现的?

  • CountDownLatch和CyclicBarrier有什么区别?

按照惯例,我们依旧是按照“是什么”,“怎么用”和“如何实现的”这3步来分析CountDownLatch,至于与CyclicBarrier的差异,下一篇我们再详细分析。

Tips:今天的“是什么”和“怎么用”合并了。

CountDownLatch的使用

不知道你有没有参加过那种感动老板,并伴以“提升”组织凝聚力为主旨的公司团建?通常行政会组织一场越野徒步活动,规定每个人都到达终点后才能吃饭,美名其曰“不抛弃不放弃的团队精神”。而老板会早早的在终点拿着花名册等待,当员工到达终点后,在花名册上划掉自己的名字,当最后一名员工到达终点后,还要敲响锣鼓,告知老板可以开始下一轮的折磨了。

19.详解AQS家族的成员:CountDownLatch

那么这样一场越野徒步活动就可以用CountDownLatch来进行简单的代码描述:

CountDownLatch countDownLatch = new CountDownLatch(10);

// 10个人进行越野徒步
for (int i = 0; i < 10; i++) {
  int finalI = i;
  new Thread(() -> {
    try {
      // 每个人比前一个选手晚1秒
      TimeUnit.SECONDS.sleep((finalI + 1));
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    System.out.println("选手[" + finalI + "]到达终点!!!");
    countDownLatch.countDown();
  }).start();
}

// 老板在目的地吃瓜,等待每个选手到达
countDownLatch.await();
// 开饭啦!
System.out.println("老板说:所有人都到齐了,午饭是每人一个吐司!!!");

看到这里,参加过此类团建活动的小伙伴是不是血压有些高了?但是你先别高,因为在这样一场血压飙升的团建中,我们已经不知不觉的掌握了CountDownLatch的用法了。

我们先试着从名字来理解CountDownLatch,CountDownLatch是一个组合词,CountDown译为“倒计时”,Latch译为“门闩”,结合起来就是倒计时结束后打开门闩(进行后续的动作)。再来看Doug Lea是如何解释CountDownLatch的作用的:

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

CountDownLatch是一个同步辅助工具,它允许一个或多个线程等待其他线程完成操作(进而执行后续操作)。

需要注意的是,CountDownLatch允许一个或多个线程进入等待,我们只需要在不同的线程中调用CountDownLatch.await就可以实现多个线程的等待。

CountDownLatch的原理

先来看作为AQS家族的成员,CountDownLatch是如何与AQS产生联系的:

19.详解AQS家族的成员:CountDownLatch

很熟悉的结构,与ReentrantLock和Semaphore一样,都是内部的同步器类Sync继承了AQS,但不同的是CountDownLatch中的Sync不再是抽象类。

19.详解AQS家族的成员:CountDownLatch

既然是继承自AQS,并且内部有计数器(倒计数也是计数)的使用,那么我们就再次搬出《AQS的今生,构建出JUC的基础》中那段关于同步状态作为计数器特性的说明:

AQS中,state不仅用作表示同步状态,也是某些同步器实现的计数器,如:Semaphore中允许通过的线程数量,ReentrantLock中可重入特性的实现,都依赖于state作为计数器的特性。

虽然没有举CountDownLatch的例子,但我知道在经过Semaphore的分析后你一定能够猜到CountDownLatch是如何使用同步状态作为计数器特性的。接下来我们就一起来看一下同步状态在CountDownLatch中的应用。

构造方法

通过AQS家族成员的类图可以看到,CountDownLatch中的同步器Sync并没有公平与非公平的区别,因此构造器只需要提供设置计数的能力即可:

public class CountDownLatch {
  public CountDownLatch(int count) {
    if (count < 0){
      throw new IllegalArgumentException("count < 0");
    }
    this.sync = new Sync(count);
  }
  
  private static final class Sync extends AbstractQueuedSynchronizer {
    Sync(int count) {
      setState(count);
    }
  }
}

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
  protected final void setState(int newState) {
    state = newState;
  }
}

不出所料,CountDownLatch的计数依旧是回归到了AQS的state上。

countDown方法

回到徒步活动中,员工到达终点后,需要在花名册上划掉自己的名字,最后一名到达后还要敲响锣鼓。在代码实现中,我们使用了CountDownLatch.countDown表示员工到达的状态,并执行相应的动作:

public class CountDownLatch {
  public void countDown() {
    sync.releaseShared(1);
  }
  
  private static final class Sync extends AbstractQueuedSynchronizer {
    protected boolean tryReleaseShared(int releases) {
      for (;;) {
        // 获取同步状态
        int c = getState();
        // 同步状态为0,返回失败
        if (c == 0){
          return false;
        }
        // 计数减1,并通过CAS更新
        int nextc = c - 1;
        if (compareAndSetState(c, nextc)) {
          // 计数器为0时返回true
          return nextc == 0;
        }
      }
    }
  }
}

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
  public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
      doReleaseShared();
      return true;
    }
    return false;
  }
}

回忆下《详解AQS家族的成员:Semaphore》中Semaphore#release方法的实现,是不是觉得似曾相识?同样是执行Sync#tryReleaseShared方法,并在成功后调用AQS的doReleaseShared方法。区别是Semaphore#tryReleaseShared的实现是计数加1,而CountDownLatch#tryReleaseShared实现是计数减1。

我们注意另一个问题,CountDownLatch的Sync#tryReleaseShared方法只有在计数器减为0时才会返回true,此时能进入AQS的doReleaseShared方法,否则都只是执行了计数器减一的操作。

此外,我们也知道AQS的doReleaseShared方法起到了唤醒AQS等待队列中节点的作用,也就是说只有在计数器减为0时,CountDownLatch才会执行一次唤醒工作

Tips:AQS的doReleaseShared已经在《详解AQS家族的成员:Semaphore》中分析过了,就不再赘述了~~

await方法

我们知道老板一早就乘车到达了终点等待,那么老板是如何判断自己要等待呢?老板提前抵达终点后,拿出花名册统计到达人数,当发现还有人没有到达终点时,他就准备打个盹,睡一觉。

我们使用了CountDownLatch.await表示老板进入等待状态:

public class CountDownLatch {
  public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
  }
}

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
  public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
    if (Thread.interrupted()) {
      throw new InterruptedException();
    }
    if (tryAcquireShared(arg) < 0) {
      doAcquireSharedInterruptibly(arg);
    }
  }
}

是不是还是很眼熟?与Semaphore一样使用了AQS的acquireSharedInterruptibly方法,那我们重点关注CountDownLatch的Sync#tryAcquireShared方法:

public class CountDownLatch {
  private static final class Sync extends AbstractQueuedSynchronizer {
    protected int tryAcquireShared(int acquires) {
      // 同步状态为0返回1,不为0返回-1
      return (getState() == 0) ? 1 : -1;
    }
  }
}

该方法对同步状态做出了判断,结合AQS的acquireSharedInterruptibly方法我们可以得到以下结论:

  • 当同步状态等于0时,tryAcquireShared返回1,不执行doAcquireSharedInterruptibly,即执行了足够次数的countDownLatch#countDown,无需进入等待队列;

  • 当同步状态不等于0时,tryAcquireShared返回-1,执行doAcquireSharedInterruptibly,即尚未执行足够次数的countDownLatch#countDown,需要进入等待队列。

简单来说就是在调用CountDownLatch#await方法时计数器不为0构建等待队列,为0就什么也不执行。

Tips:AQS的doAcquireSharedInterruptibly已经在《详解AQS家族的成员:Semaphore》中分析过了,就不再赘述了~~

结语

关于CountDownLatch的内容到这里就结束了,内容并不多。当我们不熟悉AQS的时候,不认识CountDownLatch的时候,会觉得CountDownLatch是一种“挺高级”的工具,但当我们深入其中时就会发现,“高级”的技术其实并不难学。

好了,如果本文对你有帮助的话,还希望你不要吝啬点赞。最后欢迎大家关注分享硬核技术的金融摸鱼侠王有志,以及关注我的专栏《Java面试都问啥?》,我们下次再见!文章来源地址https://www.toymoban.com/news/detail-480757.html

到了这里,关于19.详解AQS家族的成员:CountDownLatch的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java知识学习13(AQS详解)

    1、AQS介绍? AQS 的全称为 AbstractQueuedSynchronizer ,翻译过来的意思就是 抽象队列同步器 。这个类在 java.util.concurrent.locks 包下面。 AQS 就是一个抽象类,主要 用来构建锁和同步器 。 AQS 为构建锁和同步器提供了一些通用功能的实现,因此,使用 AQS 能简单且高效地构造出应用广

    2024年02月13日
    浏览(32)
  • java并发编程 AbstractQueuedSynchronizer(AQS)详解一

    AQS在类的注释上说的已经很明白,提供一个框架,用于实现依赖先进先出(FIFO)等待队列的阻塞锁和相关同步器(信号量、事件等)。此类被设计做为大多数类型的同步器的一个有用的基础类,这些同步器依赖于单个原子int值(state字段)来表示状态。 java 并发编程系列文章

    2024年02月10日
    浏览(33)
  • [当人工智能遇上安全] 8.基于API序列和机器学习的恶意家族分类实例详解

    您或许知道,作者后续分享网络安全的文章会越来越少。但如果您想学习人工智能和安全结合的应用,您就有福利了,作者将重新打造一个《当人工智能遇上安全》系列博客,详细介绍人工智能与安全相关的论文、实践,并分享各种案例,涉及恶意代码检测、恶意请求识别、

    2024年02月09日
    浏览(30)
  • [当人工智能遇上安全] 9.基于API序列和深度学习的恶意家族分类实例详解

    您或许知道,作者后续分享网络安全的文章会越来越少。但如果您想学习人工智能和安全结合的应用,您就有福利了,作者将重新打造一个《当人工智能遇上安全》系列博客,详细介绍人工智能与安全相关的论文、实践,并分享各种案例,涉及恶意代码检测、恶意请求识别、

    2024年02月04日
    浏览(42)
  • [网络安全提高篇] 一二二.恶意样本分类之基于API序列和机器学习的恶意家族分类详解

    终于忙完初稿,开心地写一篇博客。 “网络安全提高班”新的100篇文章即将开启,包括Web渗透、内网渗透、靶场搭建、CVE复现、攻击溯源、实战及CTF总结,它将更加聚焦,更加深入,也是作者的慢慢成长史。换专业确实挺难的,Web渗透也是块硬骨头,但我也试试,看看自己未

    2024年02月12日
    浏览(50)
  • [网络安全提高篇] 一二三.恶意样本分类之基于API序列和深度学习的恶意家族分类详解

    终于忙完初稿,开心地写一篇博客。 “网络安全提高班”新的100篇文章即将开启,包括Web渗透、内网渗透、靶场搭建、CVE复现、攻击溯源、实战及CTF总结,它将更加聚焦,更加深入,也是作者的慢慢成长史。换专业确实挺难的,Web渗透也是块硬骨头,但我也试试,看看自己未

    2024年02月12日
    浏览(39)
  • 7年时间,从功能测试到测试开发月薪30K,有志者事竟成

    突破自己的技术瓶颈并不是一蹴而就,还是需要看清楚一些东西,这里也有一些经验和见解跟大家分享一下。同样是职场人士,我也有我的经历和故事。在工作期间 ,我有过2年加薪5次的小小“战绩”(同期进入公司的员工,加薪不超过2次的员工大有人在),7年的时间顺利

    2024年02月07日
    浏览(45)
  • [C++]六大默认成员函数详解

    ☃️个人主页:fighting小泽 🌸作者简介:目前正在学习C++和Linux 🌼博客专栏:C++入门 🏵️欢迎关注:评论👊🏻点赞👍🏻留言💪🏻 如果一个类中什么都没有,简称空类。 但它并不是什么都没有,任何类在什么都不写的情况下, 编译器会自动生成以下6个默认成员函数。

    2024年02月04日
    浏览(32)
  • 需求管理全过程流程图及各阶段核心关注点详解

    分析报告指出,多达76%的项目失败是因为差劲的需求管理,这个是项目失败的最主要原因,比落后的技术、进度失控或者混乱的变更管理还要关键。 很多项目往往在开始的时候已经决定了失败,谜底就在谜面上,开始就注定的失败,你后面多努力都很难,注定背锅! 很多P

    2024年02月15日
    浏览(32)
  • C++ ------类和对象详解六大默认成员函数

    如果我们定义一个类,然后这个类中什么也没有。那么这里的类就什么也没有吗?其实不然,任何类在里面什么都不写时,编译器都会生成6个默认成员函数。 用户没有显式实现,编译器会生成的成员函数称为默认成员函数。 六个默认成员函数 我们来看一个Date类 观察上述代

    2024年02月15日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包