深入浅出JUC常用同步器

server/2024/11/13 20:21:11/

文章目录

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

JUC_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()方法

java">	    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() 方法是如何让线程陷入阻塞的。

java">    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.ppmy.cn/server/141318.html

相关文章

【算法】【优选算法】二分查找算法(下)

目录 一、852.⼭脉数组的峰顶索引1.1 二分查找1.2 暴力枚举 二、162.寻找峰值2.1 二分查找2.2 暴力枚举 三、153.寻找旋转排序数组中的最⼩值3.1 二分查找3.2 暴力枚举 四、LCR 173.点名4.1 二分查找4.2 哈希表4.3 暴力枚举4.4 位运算4.5 数学&#xff08;求和&#xff09; 一、…

计算机毕业设计Python+图神经网络考研院校推荐系统 考研分数线预测 考研推荐系统 考研爬虫 考研大数据 Hadoop 大数据毕设 机器学习 深度学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

机器学习(基础1)

数据集 sklearn玩具数据集 数据量小&#xff0c;数据在sklearn库的本地&#xff0c;只要安装了sklearn&#xff0c;不用上网就可以获取 sklearn现实世界数据集 数据量大&#xff0c;数据只能通过网络获取&#xff08;为国外数据集&#xff0c;下载需要梯子&#xff09; skle…

抓包分析:wireshark抓不到TLS1.3数据包中证书的解决方案

近日工作中遇到需要分析使用TLS1.3协议进行通信的数据包的情况&#xff0c;但使用wireshark进行分析发现不能抓到服务端证书&#xff0c;感到诧异遂设法解决 这篇博客给出解决方案&#xff0c;和简单的原理分析 解决方案&#xff1a; 第一步&#xff1a;在任意合适路径新建一…

Ubuntu20.04离线安装nginx

文章目录 一、gcc/g、make依赖包安装1.1 在有网的ubuntu机器上下载依赖包1.2 离线安装依赖包 二、nginx相关依赖包安装2.1 有网机器上下载安装包2.2 上传压缩包并解压2.3 安装pcre2.4 安装zlib2.5 安装openssl2.6 安装nginx 三、nginx启动验证 一、gcc/g、make依赖包安装 1.1 …

【智慧出行】微信小程序智慧旅游服务平台,轻松规划旅程

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

【神经科学学习笔记】基于分层嵌套谱分割(Nested Spectral Partition)模型分析大脑网络整合与分离的局部指标(二)

前言 1.学习背景 前几天笔者学习使用NSP (Network Segregation and Partnership) 算法计算大脑整合分离的全局指标&#xff0c;现在要在之前学习的基础上再来玩玩局部指标。 局部指标的计算主要在两个层面上进行&#xff1a;第一个层面是针对每个独立ROI的指标计算&#xff0…

解决”重复文件名重命名“问题【根据Word系统方式】

提示&#xff1a;工作中遇到的功能需求&#xff0c;在此记录&#xff0c;不喜勿喷&#xff01;谢谢 文章目录 前言一、需求分析二、需求实现 前言 最近工作中遇到的我认为有必要记录的需求实现&#xff0c;希望可以帮助到有同样需求的小伙伴们&#xff01; 提示&#xff1a;以…