跟面试官讲解CountDownLatch源码设计原理

简介: 跟面试官讲解CountDownLatch源码设计原理

1 基本设计

一种同步辅助,允许一或多个线程等待,直到在其它线程中执行的一组操作完成。

用给定的 count 初始化。由于调用countDown(),await 方法会阻塞,直到当前计数为0,之后释放所有等待线程,并立即返回任何后续的 await 调用。计数无法重置,若需重置计数,可使用CyclicBarrier。


CountDownLatch 是一种通用的同步工具,可用于:


count为1时初始化的CountDownLatch用作简单的门开关:所有调用wait的线程都在门口等待,直到调用countDown()的线程打开它

初始化为N的CountDownLatch可用来让一个线程等待,直到N个线程完成某动作或某动作已完成N次

CountDownLatch的一个有用的特性是,它不需要调用倒计时的线程等待计数达到0才继续,它只是防止任何线程继续等待,直到所有线程都通过。

2 类架构

image.png

CountDownLatch并未显式继承什么接口或类。

构造器

构造一个用给定计数初始化的CountDownLatch。

image.png

参数 count :在线程通过await()前,必须调用countDown()的次数。


CountDownLatch 的 state 并非 AQS 的默认值 0,而是可赋值的,即在 CountDownLatch 初始化时,count 就代表 state 的初始值。


new Sync(count) 就是调用了内部类 Sync 的如下构造器

image.png

count 表示我们希望等待的线程数,可能是等待一组线程全部启动或执行完成。

内部类

和 ReentrantLock 一样,CountDownLatch类也存在一个内部同步器 Sync,继承了 AbstractQueuedSynchronizer

唯一字段:

image.png

private static final class Sync extends AbstractQueuedSynchronizer {
  ...
  // 返回当前计数
    int getCount() {
        return getState();
    }
  // 共享模式下获取锁
    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }
  // 共享模式下的释放锁
    protected boolean tryReleaseShared(int releases) {
        // Decrement count; signal when transition to zero
        for (;;) {
          // 获取锁状态
            int c = getState();
            // 锁未被任何线程持有
            if (c == 0)
                return false;
            // 对 state 进行递减,直到 state 变成 0;当 state 递减为 0 时,才返回 true    
            int nextc = c-1;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }
}

3 await

可以叫做等待,也可以称为加锁。

3.1 无参

image.png

使得当前线程等待,直到锁存器 count 为0,除非线程被中断。

若当前计数为零,则此方法立即返回。


若当前线程数>0,则当前线程将出于线程调度的目的而被禁用,并处于睡眠状态,直到发生如下情况之一:


由于调用countDown()方法,count为0

其他线程中断了当前线程

若当前线程:


在进入此方法时,已设置其中断状态

或在等待时被中断

就会抛 InterruptedException,并清除当前线程的中断状态。


无参版 await 内部使用的是 acquireSharedInterruptibly 方法,实现在 AQS 中的 final 方法:

image.png

使用CountDownLatch 的内部类 Sync 重写的tryAcquireShared 尝试获得锁,若获取到锁直接返回,获取不到走2

获取锁失败,用 Node 封装一下当前线程,追加到同步队列尾部,等待在合适时机去获得锁,本步已完全实现在 AQS 中

3.2 带超时参数

最终都会转化成ms

image.png

相比于无参版本,或者指定的等待时间已过。

如果当前计数为零,则此方法立即返回值 true。

如果当前线程数大于0,则当前线程将出于线程调度的目的而禁用,并处于休眠状态,直到发生以下三种情况之一:


由于调用了countDown()方法,计数为零;或

其他一些线程中断当前线程;或

指定的等待时间已经过了

如果计数为零,则该方法返回值true。


若当前线程:


在进入此方法时已设置其中断状态;或

在等待时中断,

就会抛出InterruptedException,并清除当前线程的中断状态。

如果指定的等待时间过期,则返回false值。如果时间小于或等于0,则该方法根本不会等待。


使用的是 AQS# tryAcquireSharedNanos 方法

image.png

获得锁时,state 的值不会发生变化,像 ReentrantLock 在获得锁时,会把 state + 1,但 CountDownLatch 不会

4 countDown

降低锁存器的计数,如果计数为 0,则释放所有等待的线程。

如果当前计数大于零,则递减。如果新计数为零,那么所有等待的线程都将重新启用,以便进行线程调度。

如果当前计数等于0,则什么也不会发生。

image.png

releaseShared 已经完全实现在 AQS

image.png

主要分成两步:

  1. 尝试释放锁(tryReleaseShared),锁释放失败直接返回,释放成功走 2,本步由 Sync 实现
  2. 释放当前节点的后置等待节点,该步 AQS 已经完全实现

5 最佳实践

Kafka中的线程控制代码大量使用CountDownLatch实现优雅的线程启动、线程关闭等操作。

6 总结

研究完 CountDownLatch 的源码,可知其底层结构仍然依赖了 AQS,对其线程所封装的结点是采用共享模式,而 ReentrantLock 是采用独占模式。

目录
相关文章
|
2月前
|
消息中间件 存储 缓存
大厂面试高频:Kafka 工作原理 ( 详细图解 )
本文详细解析了 Kafka 的核心架构和实现原理,消息中间件是亿级互联网架构的基石,大厂面试高频,非常重要,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:Kafka 工作原理 ( 详细图解 )
|
1月前
|
存储 SQL 关系型数据库
MySQL进阶突击系列(03) MySQL架构原理solo九魂17环连问 | 给大厂面试官的一封信
本文介绍了MySQL架构原理、存储引擎和索引的相关知识点,涵盖查询和更新SQL的执行过程、MySQL各组件的作用、存储引擎的类型及特性、索引的建立和使用原则,以及二叉树、平衡二叉树和B树的区别。通过这些内容,帮助读者深入了解MySQL的工作机制,提高数据库管理和优化能力。
|
5月前
|
JavaScript 前端开发
【Vue面试题二十五】、你了解axios的原理吗?有看过它的源码吗?
这篇文章主要讨论了axios的使用、原理以及源码分析。 文章中首先回顾了axios的基本用法,包括发送请求、请求拦截器和响应拦截器的使用,以及如何取消请求。接着,作者实现了一个简易版的axios,包括构造函数、请求方法、拦截器的实现等。最后,文章对axios的源码进行了分析,包括目录结构、核心文件axios.js的内容,以及axios实例化过程中的配置合并、拦截器的使用等。
【Vue面试题二十五】、你了解axios的原理吗?有看过它的源码吗?
|
9天前
|
Java Linux 调度
硬核揭秘:线程与进程的底层原理,面试高分必备!
嘿,大家好!我是小米,29岁的技术爱好者。今天来聊聊线程和进程的区别。进程是操作系统中运行的程序实例,有独立内存空间;线程是进程内的最小执行单元,共享内存。创建进程开销大但更安全,线程轻量高效但易引发数据竞争。面试时可强调:进程是资源分配单位,线程是CPU调度单位。根据不同场景选择合适的并发模型,如高并发用线程池。希望这篇文章能帮你更好地理解并回答面试中的相关问题,祝你早日拿下心仪的offer!
26 6
|
30天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
5月前
|
安全 Java 容器
【Java集合类面试二十七】、谈谈CopyOnWriteArrayList的原理
CopyOnWriteArrayList是一种线程安全的ArrayList,通过在写操作时复制新数组来保证线程安全,适用于读多写少的场景,但可能因内存占用和无法保证实时性而有性能问题。
|
2月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
2月前
|
存储 安全 Java
面试高频:Synchronized 原理,建议收藏备用 !
本文详解Synchronized原理,包括其作用、使用方式、底层实现及锁升级机制。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
面试高频:Synchronized 原理,建议收藏备用 !
|
5月前
|
JavaScript 前端开发
【Vue面试题二十七】、你了解axios的原理吗?有看过它的源码吗?
文章讨论了Vue项目目录结构的设计原则和实践,强调了项目结构清晰的重要性,提出了包括语义一致性、单一入口/出口、就近原则、公共文件的绝对路径引用等原则,并展示了单页面和多页面Vue项目的目录结构示例。
|
3月前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?