深入浅出JUC常用同步器

文章目录

    • 1.JUC下同步器
      • 1.1 CountdownLatch 倒计数锁存器
      • 1.2 CyclicBarrier回环屏障
      • 1.3 Semephone 信号量
    • 2.小结

1.JUC下同步器

   日常开发会遇到主线程开启多个子线程去并行执行任务,并且主线程需要等待所有子线程执行完后在进行汇总的场景。
   同步器出现之前,通常采用Thread.join()方法来实现,join方法不够灵活,JDK大佬就在JUC下新建了几个同步器,底层都是基于AQS实现。
   关于AQS可以看看这篇一文带你看懂Java多线程并发,深度剖析AQS源码

下面就针对JUC下三种常见同步器进行简要介绍。

1.1 CountdownLatch 倒计数锁存器

这个同步器相对比较简单,先使用构造方法初始化共享锁数count,然后每次调用countDown()方法, 内部调用sync.releaseShared(1)释放一把锁,锁数减一,直到锁Count等于0则会唤醒之前使用await()方法阻塞的线程。

先看这个tryReleaseShared()方法

	    public void countDown() {sync.releaseShared(1);}public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {signalNext(head);return true;}return false;}// 着重看这个方法protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {// 当前锁数int c = getState();// 锁已经为0 则不在执行减一,避免多线程下重复减一到负数if (c == 0)return false;// 锁减一int nextc = c - 1;// CAS操作原子性保证一个线程执行if (compareAndSetState(c, nextc))// 如果为0则为true那么将执行signalNext(head)方法return nextc == 0;}}// h 参数为head 头结点。 private static void signalNext(Node h) {Node s;// 如果头结点下一个节点不为空。if (h != null && (s = h.next) != null && s.status != 0) {// 取消WAITTING状态 转为唤醒状态s.getAndUnsetStatus(WAITING);// 唤醒s节点所对应的线程。LockSupport.unpark(s.waiter);}// CAS Node 节点 部分属性如下abstract static class Node {volatile Node prev;       // initially attached via casTailvolatile Node next;       // visibly nonnull when signallableThread waiter;            // visibly nonnull when enqueuedvolatile int status;      // written by owner, atomic bit ops by others}}

再接着看await() 方法是如何让线程陷入阻塞的。

    public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}public final void acquireSharedInterruptibly(int arg)throws InterruptedException {// 正常Thread没有调用中断方法,会执行tryAcquireShared 方法if (Thread.interrupted() ||(tryAcquireShared(arg) < 0 &&acquire(null, arg, true, true, false, 0L) < 0))throw new InterruptedException();}// 初始化CountDownLatch对象时,getState()的值就已经发生变化// 因此这个通常都是返回-1。protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}// 正常逻辑都是走这个方法,代码量太大,着重分析部分关键信息。
final int acquire(Node node, int arg, boolean shared,boolean interruptible, boolean timed, long time) {// 当前线程实例              Thread current = Thread.currentThread();byte spins = 0, postSpins = 0;   // retries upon unpark of first threadboolean interrupted = false, first = false;Node pred = null;               // predecessor of node when enqueuedfor (;;) {if (!first && (pred = (node == null) ? null : node.prev) != null &&!(first = (head == pred))) {if (pred.status < 0) {cleanQueue();           // predecessor cancelledcontinue;} else if (pred.prev == null) {Thread.onSpinWait();    // ensure serializationcontinue;}}if (first || pred == null) {boolean acquired;try {if (shared)acquired = (tryAcquireShared(arg) >= 0);elseacquired = tryAcquire(arg);} catch (Throwable ex) {cancelAcquire(node, interrupted, false);throw ex;}if (acquired) {if (first) {node.prev = null;head = node;pred.next = null;node.waiter = null;if (shared)signalNextIfShared(node);if (interrupted)current.interrupt();}return 1;}}Node t;if ((t = tail) == null) {           // initialize queueif (tryInitializeHead() == null)return acquireOnOOME(shared, arg);} else if (node == null) {          // allocate; retry before enqueuetry {node = (shared) ? new SharedNode() : new ExclusiveNode();} catch (OutOfMemoryError oome) {return acquireOnOOME(shared, arg);}} else if (pred == null) {          // try to enqueue// 节点waiter存放当前线程实例。node.waiter = current;node.setPrevRelaxed(t);         // avoid unnecessary fenceif (!casTail(t, node))node.setPrevRelaxed(null);  // back outelse// t 是头节点。node就是t后置节点t.next = node;} else if (first && spins != 0) {--spins;                        // reduce unfairness on rewaitsThread.onSpinWait();} else if (node.status == 0) {// 节点状态设置为WAITING; 后面唤醒用。node.status = WAITING;          // enable signal and recheck} else {long nanos;spins = postSpins = (byte)((postSpins << 1) | 1);if (!timed)LockSupport.park(this);else if ((nanos = time - System.nanoTime()) > 0L)LockSupport.parkNanos(this, nanos);elsebreak;node.clearStatus();if ((interrupted |= Thread.interrupted()) && interruptible)break;}}return cancelAcquire(node, interrupted, interruptible);}

由于代码量太大,执行逻辑相对比较复杂,截取部分代码进行解析。

1.如果tail指针为null,初始化头结点,头结点为null 【第一个if】
2. node为空,是否共享,是则构建共享锁节点,否则构建独占锁节点。【else if】
3. pred 是前置节点 如果为空,设置当前node节点waiter 为当前线程,【else if】
node.status=WAITING; 设置当前节点状态为WAITING;

在这里插入图片描述

1.2 CyclicBarrier回环屏障

由于CountDownLatch是一次性同步方案,一旦计数器state=0后续在调用CountDown()方法就没用了,因此JDK大佬又创建了CyclicBarrier,可以通过reset()重置状态,让一组线程同步后可继续同步执行。适用于分段任务有序执行场景,这里对源码就不在探讨。采用独占锁ReentrantLock实现计数器原子更新。这个同步器相当于CountDownLatch增强版本,效率一般要略低,CountDownLatch 采用CAS来保证原子性。

重置方法reset()
在这里插入图片描述

1.3 Semephone 信号量

同步计数器递增实现,默认采用非公平策略,但是还可以通过参数传递来设置公平策略,可以实现以上两种同步器功能,但是计数器不可以自动重置,相对来说,功能更加强大。以上三种同步器都是基于AQS实现,因此大家需要重点掌握AQS,则可轻松看懂同步器源码实现方式。

2.小结

  关于以上三种同步器的使用,需要根据不同应用场景进行使用。

  1. 对于分段任务或者多个线程任务执行到指定位置需要进行聚合处理的 情况,建议使用CyclicBarrier同步器,
  2. 如没有特殊需求,就是一个简单的多个线程同步采用CountdownLatch同步器,
  3. 其他情况建议使用Semephone同步器,相对来说功能更为强大。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/12288.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

工位管理新策略:Spring Boot企业级应用

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

CAN总线物理层特性详细分析

目录 1. 简介 2. CAN总线拓扑图 3. CAN硬件电路 4. CAN电平标准 5. CAN收发器-TJA1050&#xff08;高速CAN&#xff09; 6. CAN物理层特性 1. 简介 CAN总线&#xff08;Controller Area Network Bus&#xff09;控制器局域网总线&#xff0c;是由BOSCH公司开发的一…

2024AAAI | DiffRAW: 利用扩散模型从手机RAW图生成单反相机质量的RGB图像

文章标题&#xff1a;《DiffRAW: Leveraging Diffusion Model to Generate DSLR-Comparable Perceptual Quality sRGB from Smartphone RAW Images》 原文链接&#xff1a;DiffRAW 本文是清华大学深圳研究院联合华为发表在AAAI-2024上的论文&#xff08;小声bb&#xff1a;华…

idea出现的问题

1.idea正常的运行,但是debug失败 原因&#xff1a;debug模式中使用的jdk和你在环境变量中配置的不是同一个jdk。或者说三处地方修改一致即可 1.File/Project Structure/Project Settings/Modules中的SDK 2.File/Project Structure/Platform Settings 中的SDKS 3.Run/Debug Conf…

uni-app之数据驱动的picker选择器( uni-data-picker)之可以选择到任意级别

背景说明 uni-app 官方的插件市场有数据驱动选择器&#xff0c;可以用作多级分类的场景。本人引入插件后&#xff0c;发现&#xff0c;在h5和微信小程序都只能选择到叶子级。而在给出的官方组件示例中确并非如此。 以选择年级&#xff0c;而不选择班级。然后&#xff0c;想试试…

vue3如何修改element ui input中type属性为textarea的高度

效果&#xff1a; 方法一&#xff1a;直接使用autosize <el-input:maxlength"500":autosize"{ minRows: 5, maxRows: 5 }"type"textarea"v-model"form.description"placeholder"请输入描述"></el-input> 方法二…

紫光展锐携手上赞随身Wi-Fi,让5G触手可及

近年来&#xff0c;随着各类移动应用层出不穷&#xff0c;人们对随时随地上网的需求日益增强&#xff0c;随身 Wi-Fi 设备以其便捷性、灵活性和相对较低的成本&#xff0c;成为用户满足办公、社交、娱乐等多元化需求的重要工具。5G技术的逐步普及为随身Wi-Fi市场注入了新的活力…

第四十三章 Vue之mapMutations简化mutations操作

目录 一、引言 二、完整代码 2.1. App.vue 2.2. main.js 2.3. Son1.vue 2.4. Son2.vue 2.5. index.js 一、引言 本章节我们通过掌握辅助函数mapMutations&#xff0c;来简化前面章节中调用mutations函数的繁琐方式。mapMutations 和 mapState很像&#xff0c;它是把位于…

C++编程语言:抽象机制:派生类(Bjarne Stroustrup)

第20章 派生类(Dirived Classes) 目录 20.1 引言 20.2 派生类 20.2.1 类成员函数 20.2.2 类构造函数和析构函数 20.3 派层次结构 20.3.1 类型域(Type Fields) 20.3.2 虚函数(Virtual Functions) 20.3.3 显式修饰(Explicit Qualification) 20.3.4 覆盖控制(O…

Qt--命令行终端程序开发

提示&#xff1a;本文为学习记录&#xff0c;若有错误&#xff0c;请联系作者&#xff0c;谦虚受教。 文章目录 前言一、头文件二、cpp文件三、使用流程如图所示 总结 前言 Constant dropping wears the stone. 一、头文件 #ifndef TERMINALWIDGET_H #define TERMINALWIDGET_…

一文了解珈和科技在农业遥感领域的服务内容和能力

2020年&#xff0c;农业农村部、中央网信办联合印发了《数字农业农村发展规划&#xff08;2019-2025年&#xff09;》&#xff0c;对数字农业农村建设作出了具体部署。其中&#xff0c;农业遥感作为推进数字农业农村的重要力量贯穿《规划》始终。 今年10月&#xff0c;农业农村…

D65【python 接口自动化学习】- python基础之数据库

day65 SQL-DQL-分组聚合 学习日期&#xff1a;20241111 学习目标&#xff1a;MySQL数据库-- 133 SQL-DQL-分组聚合 学习笔记&#xff1a; 分组聚合 总结 分组聚合的语法 分组聚合的注意事项 group by 中出现了哪个列&#xff0c;哪个列才能出现在select中的非聚合中

初学mongoDB

MongoDB 是一个开源的 NoSQL 数据库&#xff0c;由 C 语言编写。它与传统的关系型数据库不同&#xff0c;MongoDB 使用的是一种基于文档的存储模型&#xff0c;不需要定义固定的表结构&#xff0c;可以灵活地存储和管理大量的非结构化数据。下面是 MongoDB 的一些核心特性&…

DAY59||并查集理论基础 |寻找存在的路径

并查集理论基础 并查集主要有两个功能&#xff1a; 将两个元素添加到一个集合中。判断两个元素在不在同一个集合 代码模板 int n 1005; // n根据题目中节点数量而定&#xff0c;一般比节点数量大一点就好 vector<int> father vector<int> (n, 0); // C里的一…

基于Spring Boot的乡政府管理系统设计与实现,LW+源码+讲解

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装乡政府管理系统软件来发挥其高效地信息处理的作用&#xf…

【NLP】使用 PyTorch 从头构建自己的大型语言模型 (LLM)

读完这篇文章后&#xff0c;你会取得什么成就&#xff1f;你将能够自己构建和训练大型语言模型 (LLM)&#xff0c;同时与我一起编写代码。虽然我们正在构建一个将任何给定文本从英语翻译成马来语的 LLM&#xff0c;但你可以轻松地修改此 LLM 架构以用于其他语言翻译任务。 LLM…

QT打包应用程序文件步骤

QT应用程序&#xff08;.exe&#xff09;打包复制到其他电脑 在QT程序在自己电脑编译好了后&#xff0c;需要打包给其他人。这里介绍一下详细步骤&#xff1a; 确定编译器 搜了很多相关的打包教程&#xff0c;但是还是会出现“应用程序无法正常启动(0xc000007b)”这类错误。经过…

JMeter进阶篇

目录 上篇导航&#xff1a; 总目录&#xff1a; 一、逻辑控制器&#xff1a; 1.逻辑控制器和关联&#xff1a; 2.if逻辑控制器&#xff1a; 3.forEach控制器&#xff1a; 4.循环控制器&#xff1a; 二、关联&#xff1a; 1.xpath&#xff1a; 2.正则表达式提取器&…

homework 2024.11.10 math 1 - math 6

小学1年级和小学6年级数学图形结合&#xff0c;以及习惯养成&#xff0c;过程改进 6年级数学&#xff1a; 一年级数学&#xff1a;

【Linux 31】网络层协议 - IP

文章目录 &#x1f308; 一、IP 协议的基本概念⭐ 1. TCP IP 能保证数据的可靠传输⭐ 2. 如何理解 IP地址⭐ 3. 路由选择⭐ 4. 主机 & 路由器 & 节点 &#x1f308; 二、IP 协议的报头格式⭐ 1. IP 协议的报头格式⭐ 2. 如何将 IP 报头和有效载荷分离⭐ 3. 如何将有效…