当前位置: 首页 > news >正文

CountDownLatch的原理

使用CountDownLatch可以实现等待多个线程执行完毕的功能,实现线程之间的协调,让它们按照我们期望的顺序执行,从而避免了可能出现的并发问题。

CountDownLatch是如何实现主线程等待子线程全部结束的呢?

在这里插入图片描述

代码用例
这里我们使用一段测试代码来理解它的原理,demo 如下:

public static void main(String[] args) throws InterruptedException {int nThreads = 5; // 需要等待的线程数CountDownLatch latch = new CountDownLatch(nThreads);Random random = new Random();for (int i = 0; i < nThreads; i++) {new Thread(() -> {// 执行任务System.out.println(Thread.currentThread().getName() + " is running...");try {Thread.sleep(random.nextInt(5000));} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " finished...");// 任务完成,计数器减1latch.countDown();}).start();}// 等待所有线程执行完毕latch.await();System.out.println("All threads have finished.");
}

这个例子中,我们创建了一个CountDownLatch实例,并将需要等待的线程数设置为5。然后,我们启动5个线程来执行任务,每个线程执行的时间并不相同,并在每个线程执行完任务后调用 countDown() 方法将计数器减1。

最后,在主线程中我们调用 await() 方法来使当前线程等待,直到所有线程完成任务并计数器减为 0 为止。

原理
关键代码其实就三行。

new CountDownLatch(5) 创建 CountDownLatch 实例,设置为state 为 5,相当于是个信号量。
latch.countDown()任务执行完成,分别减少 state。
latch.await()主线程检查其他线程是否全部执行完成,否则等待。

信号量:那是多线程同步用的,一个线程完成了某一个动作就通过信号告诉别的线程,别的线程再进行某些动作。

互斥量:这是多线程互斥用的,比如说,一个线程占用了某一个资源,那么别的线程就无法访问,知道这个线程离开,其他的线程才开始可以利用这个资源。

创建对象
在创建 CountDownLatch 实例的时候,传入一个参数 5。它实现了内部类 Sync, 并且 Sync 继承了 AbstractQueuedSynchronizer(AQS)类。这里设置的 State 其实就是 AQS 中的 state 成员变量。

public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);
}
//CountDownLatch.Sync
Sync(int count) {setState(count);
}

执行countDown
设置完线信号量之后,在每个线程执行结束后会执行 latch.countDown()将 state 减一。

private final Sync sync;
public void countDown() {sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;
}

在 releaseShared 方法中,他会尝试 state 减1,这里是一个死循环,也就是自旋锁。通过CAS的方式线程安全的修改 state的值,如果不成功,就通过自旋锁不断去尝试。

private static final class Sync extends AbstractQueuedSynchronizer {protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0)return false;int nextc = c-1;//CASif (compareAndSetState(c, nextc))return nextc == 0;}}
}

在状态更新后会进入 doReleaseShared ,他会解锁这个队列中的所有线程。正常来说,走到这里都是运行状态的。但是如果主线程先走到了 await方法,这个队列中就会唤醒主线程检查state是否等于0。

子线程执行完后就结束了,不会添加到等待队列和同步队列中。

private void doReleaseShared() {for (;;) {Node h = head;if (h != null && h != tail) {int ws = h.waitStatus;//这里说明了头节点后面是有节点的,需要被唤醒。if (ws == Node.SIGNAL) {//CAS更新状态if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))continue;            // loop to recheck casesunparkSuccessor(h);}//head.waitStatus=0的情况有两种//1、就是head节点没有及时更新,线程被唤醒之后获取到了锁,在更新head之前,又经过一轮循环执行到这。  但是如果节点没有及时更新就会退出。所以执行到这一步只可能是情况2//2、head节点及时更新了,但是到了最后一个节点,它的head.waitStatus=0else if (ws == 0 &&!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))continue;                // loop on failed CAS}//节点没有及时更新,就退出。if (h == head)                   // loop if head changedbreak;}
}

最后一步:等待
在主线程执行的时候,到达 await方法会检查当前的 state 是否等于0,如果不等于就返回 -1,进入 doAcquireSharedInterruptibly 逻辑。

public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();// 检查 state 是否等于0,不等于返回-1if (tryAcquireShared(arg) < 0)doAcquireSharedInterruptibly(arg);
}

在 doAcquireSharedInterruptibly 方法中,会通过自旋锁不断检查 state。第二次自选检查的时候会被阻塞进入队列,然后等待子线程调用 countDown 方法的时候幻醒主线程,继续自选检查。

rivate 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) {//自旋不断检查是否state=0int r = tryAcquireShared(arg);if (r >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCfailed = false;return;}}//第二次自旋,park等待if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}
}

相关文章:

CountDownLatch的原理

使用CountDownLatch可以实现等待多个线程执行完毕的功能&#xff0c;实现线程之间的协调&#xff0c;让它们按照我们期望的顺序执行&#xff0c;从而避免了可能出现的并发问题。 CountDownLatch是如何实现主线程等待子线程全部结束的呢&#xff1f; 代码用例 这里我们使用一段…...

Java新特性Stream流详解

一、概述 Stream流是Java 8 API添加的一个新的抽象&#xff0c;以一种声明性方式处理数据集合&#xff08;侧重对于源数据计算能力的封装&#xff0c;并且支持序列与并行两种操作方式&#xff09;。 Stream流是对集合&#xff08;Collection&#xff09;对象功能的增强&#xf…...

关于VScode中一些常用的快捷操作!

vscode CTRLO&#xff1a;打开文件夹以开始工作 先CTRLK 再CTRLO&#xff1a;打开文件夹以开始工作 如何选择workspace&#xff1a;file → open folder→选目标文件夹【当前工作区选择会影响代码是否能运行】 如何打开终端&#xff1a;View → terminal debug看不到变化历史&…...

Django 使用Mysql数据库

目录 Django 使用Mysql数据库本地安装Mysql数据服务安装好Pymysql服务Django配置数据库迁移各种报错无法找到mysqlclient数据库拒绝连接 Django 使用Mysql数据库 本地安装Mysql数据服务 安装好Pymysql服务 python3 -m pip install PyMySQL官方文档介绍 Django配置 官网文档 …...

js继承的几种方式(原型链继承、构造函数继承、组合式继承、寄生组合式继承、ES6的Class类继承)

1.原型链继承 实现原理&#xff1a;子类的原型指向父类实例。子类在自身实例上找不到属性和方法时去它父类实例&#xff08;父类实例和实例的原型对象&#xff09;上查找&#xff0c;从而实现对父类属性和方法的继承 缺点&#xff1a; 子类创建时不能传参&#xff08;即没有…...

AnyTransition/过渡动画, MatchedGeometryEffect/匹配几何动画效果 的使用

1. AnyTransition 过渡动画效果 1.1 创建过度动画案例 AnyTransitionBootcamp.swift import SwiftUI/// 旋转修饰 View struct RotateViewModifier :ViewModifier{let rotation: Doublefunc body(content: Content) -> some View {content.rotationEffect(Angle(degrees: r…...

mac版postman升级后数据恢复办法

postman升级了一下&#xff0c;所有的collections都丢失了。 首先在finder里找到这个路径 /Users/{用户名}/Library/Application Support/Postman找到升级之前的的最新的backup.json&#xff0c;然后在postman里import这个文件。 所有升级前的collections都恢复了&#xff0…...

四.镜头知识之放大倍率

四.镜头知识之放大倍率 文章目录 四.镜头知识之放大倍率4.0 前言4.1 镜头的光学放大倍率的计算方法4.2 显示器的电子放大倍率4.2.1 智能硬件产品的显示放大倍率计算案例4.3 系统放大倍率4.4 智能硬件产品的系统放大倍率计算案例4.4 智能硬件产品的系统放大倍率计算案例4.0 前言…...

Jenkins UI 自动化持续化集成测试

一&#xff1a;安装jenkins 环境 在官网下载msi 直接安装即可 二&#xff1a;设置全局变量 设置allure 路径 三&#xff1a;创建项目 1、创建自由风格项目 2、如果项目在本地&#xff0c;且本地服务器是windows &#xff0c;找到Jenkins安装根目录&#xff0c;寻找config…...

vue项目中引入地图的详细教程

第一步&#xff1a;在项目中安装地图插件 npm i amap/amap-jsapi-loader --save 第二步&#xff1a;创建一个容器 添加id属性 &#xff08;因为地图必须使用id 不能使用class&#xff09; <div id"maps"></div> 第三步&#xff1a;给这个容器设置宽…...

MyBatisPlus 多数据源配置

目录 一、mybatis-plus 简介 特性 二、支持数据库&#xff1a; 三、 开发实例 1. 引入依赖&#xff1a; 2. 参数配置application.yml 3. 在 Spring Boot 启动类中添加 MapperScan 注解&#xff0c;扫描 Mapper 文件夹&#xff1a; 4. 编写实体类 User.java&#xff08;此处…...

使用Golang实现HTTP代理突破IP访问限制

引言 在当今互联网时代&#xff0c;网站和服务商为了维护安全性和保护用户隐私&#xff0c;常常会对特定的IP地址进行封锁或限制。但是&#xff0c;有时候我们可能需要访问这些被限制的网站或服务。为了突破这种限制&#xff0c;我们可以使用HTTP代理来隐藏真实的客户端IP地址…...

Iterator和ListIterator的区别是什么?

Iterator 和 ListIterator 都是 Java 集合框架中的迭代器,其中 Iterator 是普遍适用于所有实现了 Iterable 接口的集合类的通用迭代器,而 ListIterator 则是专门用于遍历 List 集合的迭代器,它比 Iterator 更加强大,而且只适用于 List 集合。 以下是 Iterator 和 ListItera…...

大坑-MATLAB图片转存时需注意的点

MATLAB中图片的保存和转存有一个巨大的陷阱&#xff0c;我也是在吃了大亏后发现的&#xff0c;正常情况下&#xff0c;MATLAB跑完实验&#xff0c;生成的图片如下 放大后这样 可以方便修改坐标轴标题&#xff0c;最初我就是因为想修改坐标轴标题才给它放大的&#xff0c;因为…...

基于Lang-Chain(ChatGLM和ChatChat)知识库大语言模型的部署搭建

环境准备 阿里云个人认证后&#xff0c;可免费试用机器学习平台PAI&#xff0c;可提供适合大语言模型环境搭建的高配置服务器。 点击试用阿里云服务器 试用产品选择&#xff1a;选择交互式建模PAI-DSW 适合哪些场景 文章/知识库/帮助文档等的检索基于现有知识库实现问答… …...

个人轻博客PHP开源系统/溯雪Sxlog轻博客源码/洁干净轻/占内存极低/php源码

源码简介&#xff1a; 溯雪(sxlog)它是一款很简洁越低内存的轻博客程序&#xff0c;整个程序包不到200KB&#xff0c;占内存极小&#xff0c;比一张照片都要小很多。简洁高效&#xff0c;占用空间内存极小&#xff0c;而且它不依赖任何数据库&#xff0c;不依赖富文本编辑器&a…...

2.Vue-从零开始搭建一个vue项目

题记 从零开始搭建一个vue项目&#xff0c;以下是操作的全过程。 安装Vue CLI脚手架 打开终端&#xff0c;运行以下命令全局安装Vue CLI脚手架&#xff1a; npm install -g vue/cli 查看 Vue CLI脚手架版本&#xff1a; vue -V 注意&#xff1a;查看vue版本的命令不是vue -V&a…...

快速构建代理应对

今天我要和大家分享一个解决反爬策略升级问题的方法&#xff0c;那就是快速构建代理池。如果您是一位爬虫开发人员&#xff0c;一定深知反爬策略的烦恼。但是&#xff0c;通过构建代理池&#xff0c;您可以轻松地应对反爬策略的升级&#xff0c;让您的爬虫持续高效运行。接下来…...

【LeetCode刷题(数据结构)】:另一颗树的子树

给你两棵二叉树 root 和 subRoot 检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子…...

LeetCode 2903. 找出满足差值条件的下标 I【双指针+维护最大最小】简单

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

【神经网络】如何在Pytorch中从零开始将MNIST网络量化为8位

论文&#xff1a; Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference 下载地址&#xff1a;https://arxiv.org/pdf/1712.05877.pdf 更新:量化感知训练的博客文章是在线的&#xff0c;并在这里链接&#xff0c;通过它我们可以训…...

智慧水利:山海鲸数字孪生的革新之路

一、概念 什么是港口&#xff1f; "港口"通常指的是一个水域或岸边的设施&#xff0c;用于装载、卸载、储存和处理货物、以及提供与海上、河流或湖泊交通相关的服务。港口可以包括各种类型的码头、码头设备、仓库、货物运输设施、以及各种管理和物流设施。 什么是数…...

【unity】【VR】白马VR课堂系列-VR开发核心基础04-主体设置-XR Rig的引入和设置

接下来我们开始引入并构建XR Rig。 你可以将XR Rig理解为玩家在VR世界中的替身。 我们先删除Main Camera&#xff0c;在Hierarchy右键点击删除。 然后再在场景层右键选择XR下的XR Origin。这时一个XR Origin对象就被添加到了Hierarchy。 重设XR Origin的Position和Rotation…...

Arcgis实现Tiff合并

Arcgis实现Tiff合并 现有四幅Tiff影像 打开数据管理工具 输入使用这四幅影像 下面这个就是建立数据库&#xff0c;这个不对 点击确定 合成完毕...

将已有jar包放进maven仓库

mvn install:install-file -DfileD:\sapjco3.jar -DgroupIdcom.sap.conn.jco -DartifactIdsapjco3 -Dversion3.0.14 -Dpackagingjar...

从0开始学go第八天

gin获取URL路径参数 package main//获取path&#xff08;URL&#xff09;参数 import ("net/http""github.com/gin-gonic/gin" )func main() {r : gin.Default()r.GET("/:name/:age", func(c *gin.Context) {//获取路径参数name : c.Param(&quo…...

centos7为例进行数据盘挂载详解

以centos7为例进行数据盘挂载的操作演示&#xff0c;挂载一个200G盘 1、切换至root用户 z 2、查看要挂载的硬盘 执行sfdisk -s 或 fdisk -l可以看到有一个200G。 sfdisk -s fdisk -l 需要挂载200G的这块硬盘。 3、执行lvs查看当前的lvm信息 4、执行pvcreate /dev/sdb创建…...

网络安全——自学(黑客技术)

前言 前几天发布了一篇 网络安全&#xff08;黑客&#xff09;自学 没想到收到了许多人的私信想要学习网安黑客技术&#xff01;却不知道从哪里开始学起&#xff01;怎么学&#xff1f;如何学&#xff1f; 今天给大家分享一下&#xff0c;很多人上来就说想学习黑客&#xff0c…...

Npm——yalc本地库调试工具

全局安装 npm i -g yalc本地库发布 yalc publish项目中安装 yalc add 库名本地库更新后推送 yalc push项目中删除库 yalc remove --all...

【Java基础面试一】、为什么Java代码可以实现一次编写、到处运行?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;为什么Java代码可以实现…...

一站式做网站报价/世界搜索引擎公司排名

一.JDK环境配置 1.解压到java目录下 2.路径和上面路径一致 3.配置path路径 4.查看版本 二.Eclipse配置 1.编译版本 2.运行环境 3.默认字集 4.字体大小 转载于:https://www.cnblogs.com/lbky/p/9822770.html...

南京网站建设公司/建设网站费用

网络规划设计师学习攻略(3)——学习心态的调整<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />由于网络规划设计领域具有软硬件***、学科交叉的特点决定了考试大纲涉及的内容多、知识面广&#xff0c;对其中任何一方面的考点&am…...

常用网站开发软件/网络推广平台网站推广

目录&#xff1a;一、变量 二、变量类型 三、条件判断 四、循环 五、函数 六、模块 七、数据结构一、变量变量用来存放数据&#xff0c;语法&#xff1a;变量名 变量值&#xff0c;一般为了便于阅读&#xff0c;变量名采用数据意义数据类型来命名。如namestr 马云&#xff0c;…...

咸宁网站设计公司/seo网络营销推广公司深圳

alter any cluster 修改任意簇的权限alter any index 修改任意索引的权限alter any role 修改任意角色的权限alter any sequence 修改任意序列的权限alter any snapshot 修改任意快照的权限alter any table 修改任意表的权限alter any trigger 修改任意触发器的权限alter clust…...

成都网站制作汕头/seo比较好的公司

数据结构实验之栈与队列五&#xff1a;下一较大值&#xff08;一&#xff09; Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Discuss Problem Description 对于包含n&#xff08;1<n<1000&#xff09;个整数的序列&#xff0c;对于序列中的每一元素…...

服装定制属于什么行业/广州seo网络推广员

题目1&#xff1a; 查找最晚入职员工的所有信息 CREATE TABLE ‘employees’ ( ‘emp_no’ int(11) NOT NULL, ‘birth_date’ date NOT NULL, ‘first_name’ varchar(14) NOT NULL, ‘last_name’ varchar(16) NOT NULL, ‘gender’ char(1) NOT NULL, ‘hire_date’ date N…...