Java并发之同步三:Condition条件队列

一、总览

在这里插入图片描述

二、源码分析

2.1 人口

  public Condition newCondition() {return sync.newCondition();}
 final ConditionObject newCondition() {return new ConditionObject();}
 public class ConditionObject implements Condition, java.io.Serializable {private static final long serialVersionUID = 1173984872572414699L;// 指向条件队列的第一个node节点private transient Node firstWaiter;// 指向条件队列的最后一个node节点private transient Node lastWaiter;/*** Creates a new {@code ConditionObject} instance.*/public ConditionObject() { }}

await方法:

    public final void await() throws InterruptedException {// 判断当前线程是否是中断if (Thread.interrupted())throw new InterruptedException();// 将调用await方法的线程包装成node并加入条件队列中,并返回当前nodeNode node = addConditionWaiter();// 完全释放当前线程对应的锁,// 为什么要释放锁那?加着锁挂起后,谁还能救你那int savedState = fullyRelease(node);// 0 在condition队列挂起期间未接收中断信号// -1 在condition队列期间接收到中断信号// 1 在condition队列挂起期间未接收到中断信号,但是在迁移到“阻塞队列”过程中,接收过中断信号int interruptMode = 0;// isOnSyncQueue返回true表示当前线程对应的node已经迁移到“阻塞队列”// false 说明当前node 仍然还在条件队列中,需要继续parkwhile (!isOnSyncQueue(node)) {// 挂起当前node对应的线程,接下去去看看signal过程LockSupport.park(this);// 什么时候会被唤醒?都有哪几种情况// 1、常规路径:外部线程获取到锁之后,调用signal()方法 转移条件队列的头节点到阻塞队列,当//   这个节点获取到锁后,会唤醒// 2.转移至阻塞至阻塞队列后,发现阻塞队列中的前驱节点状态是 取消状态,此时会唤醒当前节点// 3.当前节点挂起期间时,被外部线程使用中断唤醒// checkInterruptWhileWaiting: 就算在condition队列挂起期间,线程发生中断了,// 对应的node也会被迁移到“阻塞队列”if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;}// acquiredQueued:竞争队列的逻辑if (acquireQueued(node, savedState) && interruptMode != THROW_IE)interruptMode = REINTERRUPT;// 考虑下 node.nextWaiter != null 条件什么时候成立那// 其实是node在条件队列内时,如果被外部线程中断唤醒时,会加入到阻塞队列,// 但是并未设置nextWaiter != nullif (node.nextWaiter != null) // clean up if cancelled// 清理条件队列取消状态的节点unlinkCancelledWaiters();// 条件成立:说明挂起期间 发生过中断(1.条件队列内的挂起 2.条件队列之外的挂起)if (interruptMode != 0)// 条件队列内发生中断,此外await方法抛出中断异常// 条件队列外发生的中断,交给你的业务处理,如果你不处理,那什么事也不会发生reportInterruptAfterWait(interruptMode);}
  1. 将当前线程包装成ConditionNode加入到条件队列中
  2. 释放锁资源
  3. while循环,如果当前线程对应的node已经迁移到“阻塞队列”,那就park挂起它,然后开始走acquireQueued逻辑,开始竞争锁 (这里线程什么时候会到“阻塞队列”中那)
private int checkInterruptWhileWaiting(Node node) {// thread.interrupted 返回当前线程中断标记位,并且重置当前标记为falsereturn Thread.interrupted() ?// transferAfterCancel 这个方法只有在线程是被中断唤醒时才会调用(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :0;}final boolean transferAfterCancelledWait(Node node) {// 条件成立:说明当前node一定是在条件队列内,因为signal迁移节点// 到阻塞队列时,会将节点的状态修改为0if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {// 中断唤醒的node也会被加入到阻塞队列中enq(node);// true:表示是在条件队列内被中断的return true;}// 执行到这里有几种情况?// 1、当前node已经被外部线程调用 signal方法将其迁移到阻塞队列内// 2、当前node正在被外部线程调用 signal方法将其迁移到阻塞队列中while (!isOnSyncQueue(node))Thread.yield();return false;}

signal方法

 public final void signal() {// 判断调用signal方法的线程是否是独占锁持有线程,如果不是,直接抛出异常if (!isHeldExclusively())throw new IllegalMonitorStateException();Node first = firstWaiter;if (first != null)doSignal(first);}
    private void doSignal(Node first) {do {// firstWaiter = first.nextWaiter 因为当前first马上要出条件队列// 如果当前节点的下一个节点是NULL,说明条件队列只有当前一个节点了。。// 当前出队后,整个队列就空了// 所以需要更新lastWaiter = nullif ( (firstWaiter = first.nextWaiter) == null)lastWaiter = null;// 当前first节点 出条件队列,断开和下一个节点的关系first.nextWaiter = null;// transferForSignal(first)// true:当前first节点迁移到阻塞队列成功 false迁移失败} while (!transferForSignal(first) &&(first = firstWaiter) != null);}
  final boolean transferForSignal(Node node) {// cas 修改当前节点的状态,修改为0,因为当前节点马上要迁移到 阻塞队列中// 成功:当前节点在条件队列中状态正常// 失败: 1、取消状态(线程await时,未持有锁,最终线程对应的node会设置为取消状态)//       2、node对应的线程挂起期间,被其他线程使用中断信号 唤醒过(就会主动进入到阻塞队列中),这时候也会修改状态为0if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))return false;/** Splice onto queue and try to set waitStatus of predecessor to* indicate that thread is (probably) waiting. If cancelled or* attempt to set waitStatus fails, wake up to resync (in which* case the waitStatus can be transiently and harmlessly wrong).*/Node p = enq(node);int ws = p.waitStatus;// 条件一:ws >0 成立:说明前驱节点状态在阻塞队列中是取消状态,唤醒当前节点// 条件二: 前置条件(ws <= 0) //  compareAndSetWaitStatus(p,ws,Node.SIGNAL)表示设置前驱节点状态 SIGANAL状态成功//  compareAndSetWaitStatus(p,ws,NOde.SIGNAL)返回false ==> 什么时候返回false//  当前驱node对应的线程是lockInterrupt 入队的node时,是会响应中断的,外部线程给前驱线程中断//  信号之后,前驱node状态修改为取消状态,并且执行出队逻辑//  前驱节点状态只要不是0 或者-1,那么就唤醒当前线程if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))LockSupport.unpark(node.thread);return true;}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM运行时数据区(上篇)

JVM运行时数据区可分为线程共享的堆&#xff0c;方法区和线程独享的虚拟机栈、本地方法栈、程序计时器此外还有一个单独的直接内存&#xff0c;如下图所述&#xff1a; 程序计数器 程序计数器&#xff08;Program Counter Register&#xff09;也叫PC寄存器&#xff0c;每个线…

Windows安装Rust环境(详细教程)

一、 安装mingw64(C语言环境) Rust默认使用的C语言依赖Visual Studio&#xff0c;但该工具占用空间大安装也较为麻烦&#xff0c;可以选用轻便的mingw64包。 1.1 安装地址 (1) 下载地址1-GitHub&#xff1a;Releases niXman/mingw-builds-binaries GitHub (2) 下载地址2-W…

JRT界面打开器

开发BS界面时候有个问题&#xff0c;如果新做页面还没挂菜单&#xff0c;那么测试新页面有两个办法&#xff0c;一是把菜单挂上用&#xff0c;一是手输URL。而我在开发阶段两个事都不想干&#xff0c;那么怎么解决呢&#xff1f; 以前WebLoader启动时候会启动C#写的URL辅助器 …

【elfboard linux 开发板】10. 设备树与烧录

1. 设备树介绍 设备树由一系列被命名的node和property组成 可以描述如下信息&#xff1a; CPU的数量和类别内存基地址和大小总线和桥外设连接中断控制器和中断使用情况GPIO控制器和GPIO使用情况Clock 控制器和 Clock 使用情况 由dts文件以文本方式对系统设备树进行描述&…

2023.12.10查找,线性探测法

二叉树的重构 集合实现对图的dfs,bfs复写 插入排序 霍夫曼树&#xff0c;霍夫曼编码 查找成功&#xff0c;查找失败的期望值计算 9.给定散列表大小为11&#xff0c;散列函数为H(Key)Key%11。按照线性探测冲突解决策略连续插入散列值相同的4个元素。问&#xff1a;此时该散…

小白福音!一键获取Cookie,从此不再求人!

文章目录 &#x1f4d6; 介绍 &#x1f4d6;&#x1f3e1; 环境 &#x1f3e1;&#x1f4d2; 使用方法 &#x1f4d2;⚓️ 相关链接 ⚓️ &#x1f4d6; 介绍 &#x1f4d6; 本文分享一个自己专为小白写的软件&#xff0c;该软件支持傻瓜式一件获取网页cookie 软件功能 Cookie…

STM32L051使用HAL库操作实例(14)- ADC采集电压

目录 一、前言 二、ADC外设简要说明 三、STM32CubeMX配置&#xff08;本文使用的STM32CubeMX版本为6.1.2&#xff09; 1.MCU选型 2.时钟使能 3.外部时钟配置 4.串口配置 5.ADC引脚配置 6.配置STM32CubeMX生成工程文件 7.点击GENERATE CODE生成工程文件 四、工程源码 …

上海AI实验室等开源,音频、音乐统一开发工具包Amphion

上海AI实验室、香港中文大学数据科学院、深圳大数据研究院联合开源了一个名为Amphion的音频、音乐和语音生成工具包。 Amphion可帮助开发人员研究文本生成音频、音乐等与音频相关的领域&#xff0c;可以在一个框架内完成&#xff0c;以解决生成模型黑箱、代码库分散、缺少评估…

蓝凌EIS智慧协同平台 多处SQL注入漏洞复现

0x01 产品简介 蓝凌EIS智慧协同平台是一款专为企业提供高效协同办公和团队合作的产品。该平台集成了各种协同工具和功能,旨在提升企业内部沟通、协作和信息共享的效率。 0x02 漏洞概述 由于蓝凌EIS智慧协同平台 doc_fileedit_word.aspx、frm_form_list_main.aspx、frm_butt…

Visual Studio中项目添加链接文件

这个需求在VS里面使用还真不多见&#xff0c;只是最近在做项目的版本编号的时候遇到一个头大的问题&#xff0c;我一个解决方案下面有几十个类库&#xff0c;再发布的时候这几十个类库的版本号必须要统一&#xff0c;之前我们都是在单个的AssemblyInfo.cs里面去改相关的信息&am…

企业如何做到安全又极速的分发传输大文件

在当代企业运营中&#xff0c;文件的传输和分发是至关重要的任务。然而&#xff0c;随着文件体积的增大和信息敏感性的凸显&#xff0c;企业需要找到一种既安全又能够高效传输大文件的方法。本文将深入探讨如何在企业环境中实现安全又高效的大文件传输。 一、分发大文件时需要注…

ubuntu20固定串口名称

查看串口的详细信息 udevadm info --name/dev/ttyUSB0结果&#xff1a; P: /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.2/1-1.2:1.0/ttyUSB0/tty/ttyUSB0 N: ttyUSB0 L: 0 S: serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UAR…