CountDownLatch原理剖析

1. 简介

简单描述CountDownLatch的功能,那就是

在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待

2. 实现原理

在这里插入图片描述

CountDownLatch 是通过一个计数器来实现的,当我们在 new 一个 CountDownLatch 对象的时候,需要带入该计数器值,该值就表示了线程的数量。

每当一个线程完成自己的任务后,计数器的值就会减 1 。当计数器的值变为0时,就表示所有的线程均已经完成了任务,然后就可以恢复等待的线程继续执行了。
如上图示例,用给定的计数为3进行初始化 CountDownLatch。由于调用了 countDown方法,所以在当前计数到达0之前,await方法会一直受阻塞;当计数到达0时会唤醒await方法阻塞的线程执行。

3. 源码结构

在这里插入图片描述

  • J.U.C包中的最核心部分就是AQS的实现,它是JDK并发工具的实现基石,CountDownLatch也不例外,也是基于AQS进行实现
  • await、countDown方法分别调用了AQS的doAcquireShared、doReleaseShared方法以共享锁的形式对AQS的共享变量state进行操作
  • await方法阻塞等待,CountDownLatch 的作用是允许 1 或 N 个线程等待其他线程完成执行
  • countDown方法变更计数,CountDownLatch 的计数器无法被重置

4. 实现剖析

4.1 await方法

在这里插入图片描述

  • 调用await() 方法,来使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断
  • 该方法内部使用 AQS 的 acquireSharedInterruptibly(int arg) 方法
  • 在内部类 Sync 中重写了 tryAcquireShared(int arg) 方法
  • 通过AQS中的getState() 方法,获取同步状态,其值等于计数器的值
  • 如果计数器值不等于 0,则会调用 doAcquireSharedInterruptibly(int arg) 方法,该方法为一个自旋方法会尝试一直去获取同步状态

4.2 countDown方法

在这里插入图片描述

  • 调用countDown() 方法,来改变计数器数量
  • 内部调用AQS的releaseShared() 方法
  • Sync重写了tryReleaseShared() 方法
  • 释放锁,也就是操作计数器的过程,这里使用到了CAS(compareAndSetState)进行计数更新,若更新失败则进行自旋重试直到成功为止

4.3 getCount方法

调用AQS的getState方法获取计数

5. 实战用例

模拟主线程await阻塞,等待子线程计数countDown到0唤醒主线程执行

/**
 * created by guanjian on 2020/12/28 11:19
 */
public class CountDownLatchTest {

    private static final CountDownLatch cdl = new CountDownLatch(3);

    public static void main(String[] args) throws InterruptedException {
        System.out.println("开始");

        new Thread(() -> {
            try {
                System.out.format("当前计数=%s,需等待 \n", cdl.getCount());
                Thread.sleep(1000);
                cdl.countDown();
                System.out.format("当前计数=%s,需等待 \n", cdl.getCount());
                Thread.sleep(1000);
                cdl.countDown();
                System.out.format("当前计数=%s,需等待 \n", cdl.getCount());
                Thread.sleep(1000);
                cdl.countDown();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        cdl.await(6000, TimeUnit.MILLISECONDS);
        print();
        System.out.println("结束");
    }

    private static void print() {
        System.out.println("执行了");
    }
}

【控制台输出】
开始
当前计数=3,需等待 
当前计数=2,需等待 
当前计数=1,需等待 
执行了
结束

6. 总结

  • CountDownLatch的作用实际是提供了一种多线程通信的方式
  • AQS提供了多个线程并发且产生竞态条件时共享变量的安全读写方式,且丰富地提供了共享、排他两种处理竞态线程获取共享资源的方式
  • CountDownLatch利用AQS的state共享变量作为信号量,await方法会在state不等于0时进行阻塞,countDown方法会操作state共享变量改变这个信号量的值从而来影响整个CountDownLatch的运行
  • CountdownLatch是通过共享方式对锁进行操作,通过自旋方式进行阻塞等待,通过CAS对共享变量进行更新保证原子性

7. 参考

http://www.iocoder.cn/JUC/sike/CountDownLatch/

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大摩羯先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值