Condition类的signal()方法底层原理

news/2025/2/22 2:01:23/文章来源:https://www.cnblogs.com/jock766/p/18728134

一、Condition类的signal()方法底层原理

Condition 接口的 signal 方法是用于唤醒一个在 Condition 上等待的线程。与 Object 的 notify 方法类似,signal 方法会从 Condition 的等待队列中选择一个线程并将其唤醒,使其重新尝试获取锁并继续执行


1、signal 方法的核心逻辑

signal 方法的主要逻辑可以分为以下几个步骤:

1、检查当前线程是否持有锁:调用 signal 的线程必须持有与 Condition 关联的锁。

2、从等待队列中移除第一个节点:将 Condition 等待队列中的第一个节点转移到 AQS 的同步队列中。

3、唤醒线程:通过 AQS 的机制唤醒转移后的线程,使其重新尝试获取锁。


2、signal 方法的源码分析

以下是 ConditionObject 中 signal 方法的源码及其详细分析:

public final void signal() {// 判断调用 signal 方法的线程是否是独占锁持有线程if (!isHeldExclusively())throw new IllegalMonitorStateException();// 获取条件队列中第一个 NodeNode first = firstWaiter;// 不为空就将该节点【迁移到阻塞队列】if (first != null)doSignal(first);
}


2.1、isHeldExclusively 方法

  • 检查当前线程是否独占持有与 Condition 关联的锁。

  • 如果当前线程未持有锁,抛出 IllegalMonitorStateException。


2.2、firstWaiter 变量

  • firstWaiter 是 Condition 等待队列的头节点。

  • 如果 firstWaiter 为 null,说明等待队列为空,没有需要唤醒的线程。


2.3、doSignal 方法

  • doSignal 是实际执行唤醒操作的方法。

  • 它会将等待队列中的第一个节点转移到 AQS 的同步队列中,并唤醒对应的线程。


3、doSignal 和 signalAll方法的源码分析


3.1、以下是 doSignal 方法的源码及其详细分析:

// 唤醒 - 【将第一个节点转移至 AQS 队列尾部】
private void doSignal(Node first) {do {// 成立说明当前节点的下一个节点是 null,当前节点是尾节点了,队列中只有当前一个节点了if ((firstWaiter = first.nextWaiter) == null)lastWaiter = null;first.nextWaiter = null;// 将 Condition条件队列中的 Node 转移至 AQS 队列,不成功且还有节点则继续循环} while (!transferForSignal(first) && (first = firstWaiter) != null);
}
  • 将 firstWaiter 指向下一个节点。

  • 如果等待队列为空,将 lastWaiter 设置为 null。

  • 断开当前节点的链接,将其从Condition条件队列中移除。


3.2、会调用signalAll()这个函数,唤醒所有的节点

private void doSignalAll(Node first) {lastWaiter = firstWaiter = null;do {Node next = first.nextWaiter;first.nextWaiter = null;transferForSignal(first);first = next;// 唤醒所有的节点,都放到阻塞队列中} while (first != null);
}


4、transferForSignal 方法的源码分析


transferForSignal作用

  • transferForSignal 是将节点从等待队列转移到 AQS 同步队列的核心方法。

  • 如果转移成功,返回 true;否则返回 false。


以下是 transferForSignal 方法的源码及其详细分析:

// 如果节点状态是取消, 返回 false 表示转移失败, 否则转移成功
final boolean transferForSignal(Node node) {// CAS 修改当前节点的状态,修改为 0,因为当前节点马上要迁移到阻塞队列了// 如果状态已经不是 CONDITION, 说明线程被取消(await 释放全部锁失败)或者被中断(可打断 cancelAcquire)if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))// 返回函数调用处继续寻找下一个节点return false;// 【先改状态,再进行迁移】// 将当前 node 加入 AQS阻塞队列,p 是当前节点在阻塞队列的【前驱节点】Node p = enq(node);int ws = p.waitStatus;// 如果前驱节点被取消或者不能设置状态为 Node.SIGNAL,就 unpark 取消当前节点线程的阻塞状态, // 让 thread-0 线程竞争锁,重新同步状态if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))LockSupport.unpark(node.thread);return true;
}


4.1、更新节点状态

  • 使用 CAS 操作将节点的状态从 CONDITION 更新为 0。

  • 如果更新失败,说明节点已被取消,返回 false。


4.2、将节点加入 AQS同步队列

  • 调用 enq(node) 方法将节点加入到 AQS 的同步队列中。

  • enq 方法会将节点插入到同步队列的尾部,并返回前驱节点。


4.3、唤醒线程

  • 检查前驱节点的状态:

    • 如果前驱节点的状态为 CANCELLED,或者无法将其状态更新为 SIGNAL,则直接唤醒线程。

    • 否则,线程会在同步队列中等待,直到前驱节点释放锁。


二、signal 方法的关键点


1、线程安全性

  • signal 方法必须在持有锁的情况下调用,否则会抛出 IllegalMonitorStateException。

  • 通过 AQS 的同步队列机制,确保线程安全。


2、节点转移

  • signal 方法会将节点从 Condition 的等待队列转移到 AQS 的同步队列中。

  • 转移后的节点会等待获取锁,并在获取锁后继续执行。


3、唤醒机制

  • 使用 LockSupport.unpark 唤醒线程。

  • 唤醒的线程会重新尝试获取锁,并在获取锁后从 await 方法中返回。


三、总结

Condition 的 signal 方法通过以下机制实现线程的唤醒:

1、检查当前线程是否持有锁。

2、将等待队列中的第一个节点转移到 AQS 的同步队列中。

3、唤醒对应的线程,使其重新尝试获取锁。

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

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

相关文章

【ssh隧道】利用ssh隧道将公网meterpreter弹至本地的msf中

不少人私信说如果攻击者的网络和被攻击者的网络都是位于不同地理位置的私网,这种情况下如何反弹shell,今天统一给大家介绍一款最间简单的方式。 利用自己公网VPS的SSH把meterpreter直接弹到本地的msf中 一、环境介绍 ubuntu16-LAMP:假设为入侵者公网的一台linux[vps]机器,公…

洛谷文章存档

由于洛谷专栏没了,故放博客园存档。 CSP-J/S 2024 游记 前言 2024.9.29 ~ 现在: \(\footnotesize\mathbf{安全声明:洛谷的文章只显示 {\underline{\color{red}{发布时期}}}而不显示{\underline{\color{red}{更新时期}}}!} \\ \footnotesize\mathbf{题目和答案均为{\underli…

一文了解:新基建是什么

新基建(新型基础设施建设)是中国提出的战略性发展方向,主要聚焦于数字化、智能化、绿色化等领域,旨在推动经济转型升级和高质量发展。 一、信息基础设施 以5G、人工智能、大数据等技术为核心,构建数字时代的“技术底座”:通信网络基础设施5G网络:高速率、低时延的通信网…

三大平台云数据库生态服务对决

title: 三大平台云数据库生态服务对决 date: 2025/2/21 updated: 2025/2/21 author: cmdragon excerpt: 包含自动分片算法实现、跨云迁移工具链开发、智能索引推荐系统构建等核心内容,提供成本优化计算模型、灾备演练方案设计、性能调优路线图等完整解决方案。 categories:前…

【EDR】一种对抗企业级EDR深度行为分析的双模式混淆技术

所谓双模式混淆技术,指的是利用脚本+PE格式进行混合混淆技术,该技术可以导致沙箱执行程序失败, 而且也能够加大分析难度,甚至手动执行都无法成功。 该样本原始文件名为:"Xclient.exe"和"XingCode Unblocker2025.exe", 奇怪的是, 被工具标识为是一个数据文件…

【SSH漏洞】SSH公私钥认证原理及相关漏洞

免责声明: 本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。前言 随着信息安全意识的提升,SSH(Secure Shell)作为一种安全的远程登录协议,被广泛应用于服务器管理、数据传输等领域。SSH的核心机制之一是公私钥认证,它相比密码认证更安全,能有效抵御暴力破…

亿级流量下通用的高并发架构设计

关键词:读/写分离、数据缓存、缓存更新、CQRS、数据分片、异步写前言 既然是亿级用户应用,那么高并发必然是其架构设计的核心要素。 本文我们将介绍高并发架构设计的一些通用设计方案。 一、高并发架构设计的要点 高并发意味着系统要应对海量请求。从笔者多年的面试经验来看,…

微信小程序体验版可以保存图片线上版保存图片失败

最近,在开发一款壁纸类的小程序,体验版开发版都可以保存到手机上,但是唯独线上版本保存失败,挠头俩小时终于弄好了 发现 这并不是代码上的问题,经过一番摸索,发现用户隐私保护指引中的“相册写入”权限没有更新,提交审核过后即可。虎爷壁纸小程序

【微服务】视图:如何实现服务和数据在微服务各层的协作?

在 DDD 分层架构和微服务代码模型里,我们根据领域对象的属性和依赖关系,将领域对象进行分层,定义了与之对应的代码对象和代码目录结构。分层架构确定了微服务的总体架构,微服务内的主要对象有服务和实体等,它们一起协作完成业务逻辑。 那在运行过程中,这些服务和实体在微…

让Ai写个拓扑排序C#算法

前言使用AI的一个有趣例子有个业务,在实现过程中需要使用拓扑算法,但我以前所学的东西都还给老师了,刚好这几天公司开展了使用AI的培训与分享,遂想了下,那就让AI来搞下吧。 源代码Learning-Case/TopologyAlgorithm at main qiqiqiyaya/Learning-Case 出场AI工具豆包DeepS…

百万架构师第四十一课:RabbitMq:可靠性投递和实践经验|JavaGuide

来源:https://javaguide.net RabbitMQ 2-可靠性投递与生产实践 可靠性投递 ​ 首先需要明确,效率与可靠性是无法兼得的,如果要保证每一个环节都成功,势必会对消息的收发效率造成影响。 如果是一些业务实时一致性要求不是特别高的场合,可以牺牲一些可靠性来换取效率。① 代…

jh

什么时候才能跟jh一样强啊 什么时候可以找回曾经的自己