AQS学习

news/2024/5/15 22:48:44/文章来源:https://blog.csdn.net/ywl470812087/article/details/128429805

1.1 AQS 简单介绍

AQS 的全称为(AbstractQueuedSynchronizer),这个类在 java.util.concurrent.locks 包下面。

 AQS 是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器, 比如我们提到的 ReentrantLock,Semaphore,其他的诸如 ReentrantReadWriteLock,SynchronousQueue,FutureTask(jdk1.7) 等等皆是基于 AQS 的。当然,我们自己也能利用 AQS 非常轻松容易地构造出符合我们自己需求的同步器。

1.2 AQS 原理

1.2.1 AQS 原理概览

AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒 时锁分配的机制,这个机制 AQS 是用 CLH 队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

 关于AQS类有一段注释是这样写的:

Wait queue node class.
The wait queue is a variant of a "CLH" (Craig, Landin, and Hagersten) lock queue. CLH locks are normally used for spinlocks. We instead use them for blocking synchronizers, but use the same basic tactic of holding some of the control information about a thread in the predecessor of its node. A "status" field in each node keeps track of whether a thread should block. A node is signalled when its predecessor releases. Each node of the queue otherwise serves as a specific-notification-style monitor holding a single waiting thread. The status field does NOT control whether threads are granted locks etc though. A thread may try to acquire if it is first in the queue. But being first does not guarantee success; it only gives the right to contend. So the currently released contender thread may need to rewait.
To enqueue into a CLH lock, you atomically splice it in as new tail. To dequeue, you just set the head field.

大致的意思是这样的:

等待队列节点类。

等待队列是“CLH”(Craig、Landin和Hagersten)锁定队列的变体。CLH锁通常用于自旋锁。相反,我们使用它们来阻止同步器,但使用相同的基本策略,即在其节点的前一个线程中保存一些有关线程的控制信息。每个节点中的“状态”字段跟踪线程是否应该阻塞。节点在其前任释放时发出信号。否则,队列的每个节点都充当一个特定的通知样式监视器,保存一个等待线程。状态字段不控制线程是否被授予锁等。如果线程是队列中的第一个,则线程可能会尝试获取。但第一并不能保证成功;它只赋予了竞争的权利。因此,当前发布的竞争者线程可能需要重新等待。

要排队进入CLH锁,您可以将其作为新的尾部自动拼接。要退出队列,只需设置head字段。

           +------+   prev   +-----+        +-----+
  head |         |  <----     |        | <---- |       |  tail
           +------+             +-----+        +-----+
       
 

Insertion into a CLH queue requires only a single atomic operation on "tail", so there is a simple atomic point of demarcation from unqueued to queued. Similarly, dequeuing involves only updating the "head". However, it takes a bit more work for nodes to determine who their successors are, in part to deal with possible cancellation due to timeouts and interrupts.
The "prev" links (not used in original CLH locks), are mainly needed to handle cancellation. If a node is cancelled, its successor is (normally) relinked to a non-cancelled predecessor. For explanation of similar mechanics in the case of spin locks, see the papers by Scott and Scherer at http://www.cs.rochester.edu/u/scott/synchronization/
We also use "next" links to implement blocking mechanics. The thread id for each node is kept in its own node, so a predecessor signals the next node to wake up by traversing next link to determine which thread it is. Determination of successor must avoid races with newly queued nodes to set the "next" fields of their predecessors. This is solved when necessary by checking backwards from the atomically updated "tail" when a node's successor appears to be null. (Or, said differently, the next-links are an optimization so that we don't usually need a backward scan.)

 翻译:

插入CLH队列只需要对“尾部”执行一个原子操作,所以从未排队到排队有一个简单的原子分界点。同样,退出队列只涉及更新“头部”。然而,节点需要更多的工作来确定他们的继任者是谁,部分原因是要处理由于超时和中断而可能导致的取消。

“prev”链接(未在原始CLH锁中使用)主要用于处理取消。如果某个节点被取消,则其后续节点(通常)将重新链接到未取消的前一个节点。有关自旋锁的类似力学解释,请参阅Scott和Scherer在http://www.cs.rochester.edu/u/scott/synchronization/

我们还使用“下一步”链接来实现阻塞机制。每个节点的线程id都保存在自己的节点中,因此前一个节点通过遍历下一个链接来通知下一个节点唤醒,以确定它是哪个线程。确定后一个节点必须避免与新排队的节点竞争,以设置前一个的“下一个”字段。当一个节点的后继节点看起来为空时,这可以在必要时通过从原子更新的“尾部”向后检查来解决。(或者,换言之,下一个链接是一个优化,因此我们通常不需要反向扫描。)

Cancellation introduces some conservatism to the basic algorithms. Since we must poll for cancellation of other nodes, we can miss noticing whether a cancelled node is ahead or behind us. This is dealt with by always unparking successors upon cancellation, allowing them to stabilize on a new predecessor, unless we can identify an uncancelled predecessor who will carry this responsibility.
CLH queues need a dummy header node to get started. But we don't create them on construction, because it would be wasted effort if there is never contention. Instead, the node is constructed and head and tail pointers are set upon first contention.
Threads waiting on Conditions use the same nodes, but use an additional link. Conditions only need to link nodes in simple (non-concurrent) linked queues because they are only accessed when exclusively held. Upon await, a node is inserted into a condition queue. Upon signal, the node is transferred to the main queue. A special value of status field is used to mark which queue a node is on.
Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill Scherer and Michael Scott, along with members of JSR-166 expert group, for helpful ideas, discussions, and critiques on the design of this class.

 翻译:

抵消给基本算法引入了一些保守性。由于我们必须轮询其他节点的取消,因此我们可能无法注意到被取消的节点是在我们之前还是在我们之后。这是通过在取消时始终取消后续节点的标记来解决的,允许他们稳定在一个新的前任节点上,除非我们能够确定一个未被取消的前任节点将承担此责任。

CLH队列需要一个虚拟头节点才能启动。但我们不会在施工中创建它们,因为如果不存在争议,这将浪费精力。相反,在第一次争用时构造节点并设置头指针和尾指针。

等待条件的线程使用相同的节点,但使用额外的链接。条件只需要链接简单(非并发)链接队列中的节点,因为它们只在独占时被访问。等待时,节点被插入到条件队列中。发出信号后,节点被转移到主队列。状态字段的特殊值用于标记节点所在的队列。

感谢Dave Dice、Mark Moir、Victor Luchangco、Bill Scherer和Michael Scott以及JSR-166专家组成员对本课程设计的有益想法、讨论和评论。

看个 AQS (AbstractQueuedSynchronizer)原理图借用知乎的一张图: 

AQS 使用一个 int 成员变量来表示同步状态,通过内置的 FIFO 队列来完成获取资源线程的排队工作。

AQS 使用 CAS 对该同步状态进行原子操作实现对其值的修改。

状态信息通过 protected 类型的getState , setState , compareAndSetState 进行操作

 

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

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

相关文章

五、Arduino IDE开发esp8266环境搭建

1、安装驱动程序 (1)安装USB转串口驱动程序。 (2)根据板载的USB转串口驱动芯片选择合适驱动安装。USB转串口芯片负责和电脑之间进行数据通信。 (3)常见USB转串口驱动 CP210x驱动:CP210x USB 至 UART 桥 VCP 驱动器 - 芯科科技 CH340驱动 2、Arduino IDE环境搭建 要想使用Ar…

K8S-存储-Volume

问题 容器磁盘上的文件的生命周期是短暂的&#xff0c;这就使得在容器中运行重要应用时会出现一些问题。首先&#xff0c;当容器崩溃 时&#xff0c;kubelet 会重启它&#xff0c;但是容器中的文件将丢失——容器以干净的状态&#xff08;镜像最初的状态&#xff09;重新启动。…

【kafka】学习笔记(三)

学习笔记七、Kafka-Eagle 监控7.1 环境准备7.2 Eagle 安装7.3、修改配置文件7.4、添加环境变量7.5、启动Eagle八、Kafka-Kraft 模式8.1、Kafka-Kraft 集群部署8.2、初始化集群数据目录8.3、启动 kafka 集群8.4、测试8.5、集群启动脚本九、SpringBoot集成Kafka七、Kafka-Eagle 监…

支持设备的待机唤醒功能

系统待机唤醒功能 1 说明背景 1.1 需求 支持 GPU 进入低功耗模式&#xff0c;让用户选择降低设备的功耗 1.2 概念 上位词&#xff1a;APM, ACPI 同类词&#xff1a;睡眠模式, S0~S5 下位词&#xff1a;系统挂起, 系统唤醒, 运行时设备电源管理 1&#xff09;ACPI 在计算机…

第10章_索引优化与查询优化

第10章_索引优化与查询优化 都有哪些维度可以进行数据库调优?简言之: 索引失效、没有充分利用到索引——索引建立关联查询太多JOIN (设计缺陷或不得已的需求)——SQL优化服务器调优及各个参数设置(缓冲、线程数等)———调整my.cnf。数据过多――分库分表 关于数据库调优的…

net/http 库的客户端实现(下)

前言 上一篇文章我们讲了 net/http 库客户端 request 的构建&#xff0c;接下来继续讲构建HTTP请求之后的处理操作 net/http 库的客户端实现(上) net/http 库的客户端实现(下) net/http 库的服务端实现 启动事务 构建 HTTP 请求后&#xff0c;接着需要开启HTTP事务进行请…

Python——几个常用的数学函数

1. min()函数&#xff1a;取出给定参数的最小值 说明&#xff1a;获取指定数值或者指定序列中最小值。 print(min(1, 5)) print(min(1, 2, 3, 4, 5, 6)) print(min([2, 3, 4, 5])) 2.max()函数&#xff1a;取出给定参数的最大值 说明&#xff1a;获取指定数值或者指定序列中…

XDocReport使用入门

XDocReport 简介 XDocReport是GitHub上根据麻省理工学院许可证开源的Wrod导出框架。XDocReport可以根据ODT、Doc、Docx文档模板通过模板引擎语法&#xff08;Freemarker、Velocity&#xff09;转换为另外一种格式文档&#xff08;Doc、Docx、XHTML、PDF&#xff09;。 XDocR…

前端小知识:控制台打印(console)- 模拟Java日志打印、表格形式打印美化输出对象、代码运行时间统计

文章目录6. 控制台打印&#xff08;Console&#xff09;模拟Java日志打印格式美化对象打印&#xff08;表格形式打印输出&#xff09;日志等级输出&#xff08;让其在控制台显示时有颜色提示&#xff09;代码运行时间统计打印输出6. 控制台打印&#xff08;Console&#xff09;…

用树莓派4B安装gitlab,亲测可用~

最近成功在CentOS7上安装了gitlab&#xff0c;忽然想到是不是可以把吃灰的树莓派4B也装上gitlab&#xff0c;于是研究了一下&#xff0c;做个分享。 树莓派是4B 8G版本。本身装的是官方的64位系统。之前可能还装过一些乱七八糟的东西&#xff0c;这里就不提了。 上gitlab官网…

移动 IP(计算机网络-网络层)

目录 移动性对网络应用的影响 移动IP中数据报的转发过程 移动IP中数据报的转发过程 三角路由的低效性 解决三角路由的低效性 移动IP的标准 移动性对网络应用的影响 现在先考虑这样一种情况&#xff0c;一个用户拿着无线移动设备在一个Wi-Fi服务区内走动&#xff0c;并且边…

【python圣诞树的实现】

&#x1f935;‍♂️ 个人主页老虎也淘气 个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f44d;&#x1f3fb; 收藏…

http 库的服务端实现

前言 net/http 库的客户端实现(上) net/http 库的客户端实现(下) net/http 库的服务端实现 上两篇文章介绍了 http 客户端的实现&#xff0c;这篇文章看一下服务端的实现 服务端 使用 net/http 库可以快速搭建HTTP服务&#xff0c;HTTP服务端主要包含两部分&#xff1a; …

【圣诞特辑】码一个漂漂亮亮的圣诞树(Single Dog版)

目录 前言 一、C语言版圣诞树 1.代码实现 2.效果图 二、python版圣诞树 1.代码实现 2.效果图​ 三、html5版圣诞树 1.代码实现 2.效果图 总结 前言 圣诞节即将来临&#xff0c;圣诞树也是必不可少的装饰之一。圣诞树是一棵绿叶繁茂的树&#xff0c;上面挂满了彩色的灯…

Python pandas有好几百个库函数,你都用过吗(4)

上一篇链接&#xff1a; https://blog.csdn.net/boysoft2002/article/details/128428569 S~W&#xff1a; Function46~56 Types[Function][45:] [set_eng_float_format, show_versions, test, timedelta_range, to_datetime, to_numeric, to_pickle, to_timedelta, unique,…

VSCode 最全实用插件

一、必备插件 &#x1f33e;Chinese&#xff08;中文&#xff09; Settings Sync&#xff08;配置同步到云端&#xff09; 可以让我们的vscode配置同步到云端&#xff0c;当我们跟换电脑或者再次安装vscode的时候&#xff0c;只需要登录账号即可同步配置了 wakatime&#xf…

技术分享 Oracle下启用块跟踪

创建存放块跟踪文件目录 [oraclehost01 ~]$ cd /u01/app [oraclehost01 app]$ mkdir BCT 启用块跟踪 SQL> alter database enable block change tracking using file /u01/app/BCT/rman.bct; 检查块跟踪状态 SQL> col filename for a22 SQL> select filename, status,…

RabbitMQ——延迟队列

目录 一、延迟队列的应用场景 1. 场景&#xff1a;"订单下单成功后&#xff0c;15分钟未支付自动取消" ① 传统处理超时订单 ② RabbitMQ延时队列方案 二、延迟队列中的消息投递和消息消费 1.TTL 和 DLX ① TTL ② DLX和死信队列 ③ 延迟队列 ④ 开发步骤 …

get/post/put/delete请求头说明

目录 1.请求头说明 2.get 3.delete 4.post 5.put 6. 说明 7.Content-Type说明 1.请求头说明 前端发出的请求通过浏览器进行查看&#xff0c;可以发现分为四个部分。常规信息(General)&#xff0c;请求头信息(Request Headers)&#xff0c;响应头信息(Response Headers)…

ConvLSTM时空预测实战代码详解

写在前面 时空预测是很多领域都存在的问题&#xff0c;不同于时间序列&#xff0c;时空预测不仅需要探究时间的变化&#xff0c;也需要关注空间的变化。许多预测问题都只片面的关注时间问题&#xff0c;如预测某人未来3年患某种病的概率&#xff0c;食堂就餐人数等&#xff0c…