【Interview】深入理解CountDownLatch源码

2024-05-13 07:58

本文主要是介绍【Interview】深入理解CountDownLatch源码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

类方法

概述

  • 允许一个线程的或多个线程等待其他线程完成操作。和join方法类似,初始化对象时通过传入一个固定的计数器总数,线程方法执行时调用countDown给计数器减1,当计数器0时,就会恢复等待的线程继续执行。
  • CountDownLatch的计数器不能重用。只能使用一次
    *常用的使用场景是提升程序的并行效率,同时处理多个任务后,最后需要提示任务完成。类似的表格的批量解析读取。

使用方法

一个线程等待

    static CountDownLatch c=new CountDownLatch(2);public static void main(String[] args) throws InterruptedException {System.out.println("初始化任务数:"+c.getCount());new Thread(()->{System.out.println("任务1执行");c.countDown();System.out.println("任务2执行");c.countDown();}).start();;c.await();System.out.println("任务执行完毕!");}

输出结果

初始化任务数:2
任务1执行
任务2执行
任务执行完毕!

多个线程等待

static CountDownLatch countDownLatch=new CountDownLatch(3);//汇总任务static class T1 extends Thread{@Overridepublic void run() {try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("所有表格已经读取完了,进行汇总处理");}}//批量处里表格数据任务static class task  extends Thread{@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + ":开始处理表格数据");//处理完计数器就减1countDownLatch.countDown();}}public static void main(String[] args) throws InterruptedException {new T1().start();for (int i = 1; i <=3; i++) {new task().start(); //多线程读取表格}}

输出结果

Thread-1:开始处理表格数据
Thread-2:开始处理表格数据
Thread-3:开始处理表格数据
所有表格已经读取完了,进行汇总处理

源码分析

获取一个countDownLatch时

  • 源码中可以看出是如果初始传入的j计数器为0时是直接抛出异常的;
  • 内部是通过new Sync一个内部返回一个对象的。Sync是一个内部同步器类,继承AQS。

Sync内部类

    private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 4982264981922014374L;//初始化同步状态,count就是传入的计数器Sync(int count) {setState(count);}//获取同步状态总数,就好像类似锁重入的总次数int getCount() {return getState();}/**/共享式获取同步,类似读写锁的读写,,但是这里只是获取,没有做其它操作state是一个volatile修饰的成员变量*/protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}//共享式的释放同步状态,protected boolean tryReleaseShared(int releases) {// 自旋for (;;) {int c = getState();//为0 说明计数器已经减完了 直接返回falseif (c == 0)return false;//不为0的操作。 获取当前同步状态总数减一int nextc = c-1;//CA方式设置state,成功返回trueif (compareAndSetState(c, nextc))return nextc == 0;}}}

await方法

    public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}
  • await方法是通过sync内部类调用AQS中的acquireSharedInterruptibly()方法
  • 执行await方法的线程会在计数器没有成为0时一直处于等待,除非线程被中断,支持可中断的。
    public final void acquireSharedInterruptibly(int arg)throws InterruptedException {//判断是中断了if (Thread.interrupted())throw new InterruptedException();//这里是执行内部类的tryAcquireShared方法提供了具体实现,//就是获取同步状态的值,如果获取失败就会返回-1if (tryAcquireShared(arg) < 0)//获取同步状态失败 执行如下方法,这个方法以自旋的方式一直获取同步状态doAcquireSharedInterruptibly(arg);}private void doAcquireSharedInterruptibly(int arg)throws InterruptedException {final Node node = addWaiter(Node.SHARED);boolean failed = true;try {for (;;) {final Node p = node.predecessor();if (p == head) {int r = tryAcquireShared(arg);if (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCfailed = false;return;}}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}}

countDown执行计数器减法操作

  • countDownf方法每执行一次,计数器就减1,如果计数到达零,则释放所有等待的线程
    public void countDown() {//通过内部类sync执行AQS中的共享式释放同步状态sync.releaseShared(1);}//AQS中的方法public final boolean releaseShared(int arg) {//tryReleaseShared方法是syncs实现了重写,如果返回true则说明释放同步状态失败if (tryReleaseShared(arg)) {//失败AQS  doReleaseShared方法, doReleaseShared();return true;}return false;}
  • doReleaseShared方法会依自旋的方式不断尝试释放同步状态
private void doReleaseShared() {for (;;) {Node h = head;if (h != null && h != tail) {int ws = h.waitStatus;if (ws == Node.SIGNAL) {if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))continue;          unparkSuccessor(h);}else if (ws == 0 && !compareAndSetWaitStatus(h,0,Node.PROPAGATE))continue;               }if (h == head)                   break;}}

总结

  • CountDownLatch是基于AQS实现的一个并发工具类,允许一个线程或多个线程等待其它线程操作,初始化是传入总的计数器,内部都通过new Sync一个返回一个对象。当调用countDown()方法 就会吧计数器做递减,当计数器为0时,就会恢复等待的线程继续执行,计数到达零之前,await 方法会一直受阻塞。

java知识归纳总结
github: https://a870439570.github.io/interview-docs

这篇关于【Interview】深入理解CountDownLatch源码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/985121

相关文章

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝