AQS学习

news2024/11/18 21:27:42

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.coloradmin.cn/o/112776.html

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

相关文章

多协议标签交换MPLS(计算机网络-网络层)

目录 MPLS 的优势 MPLS 首部的位置与格式 MPLS 首部的位置与格式 MPLS 转发等价类 MPLS 的优势 MPLS 的真正优点在于它的流量管理能力&#xff1a;提供沿多条路径转发分组的能力&#xff0c;并能灵活地为某些流量指定其中的一条路径 这种能力被称为显示路由&#xff0c;其…

占道经营出店摆摊监测识别 python

占道经营出店摆摊监测识别通过python基于yolov7网络架构深度学习模型&#xff0c;对现场画面中检测到出店摆摊违规经营或者流动商贩占道经营时&#xff0c;立即抓拍告警同步后台。OpenCV基于C实现&#xff0c;同时提供python, Ruby, Matlab等语言的接口。OpenCV-Python是OpenCV…

【Django】第四课 基于Django超市订单管理系统开发

概念 本文在上一文之上&#xff0c;针对管理员&#xff0c;经理&#xff0c;普通员工身份的用户操作订单管理模块功能。 功能实现 该功能也是业务功能模块&#xff0c;管理员不具备操作权限&#xff0c;普通员工需要对超市所合作的供应商进行进货&#xff0c;因此普通员工可…

数据结构与算法——Java实现排序算法(二)

数据结构与算法——Java实现排序算法&#xff08;一&#xff09;_我爱布朗熊的博客-CSDN博客 七、希尔排序&#xff08;自我感觉有点难理解&#xff09; 为了解决直接插入排序所带来的弊端&#xff0c;我们接来下看一下希尔排序 希尔排序也是一种插入排序&#xff0c;简单插入排…

口罩佩戴监测系统 yolo

口罩佩戴监测系统通过yolo网络对现场画面人员口罩佩戴情况进行识别检测。我们使用YOLO(你只看一次)算法进行对象检测。YOLO是一个聪明的卷积神经网络(CNN)&#xff0c;用于实时进行目标检测。该算法将单个神经网络应用于完整的图像&#xff0c;然后将图像划分为多个区域&#x…

科技交流英语(2022秋)Unit 5 test

科技交流英语&#xff08;2022秋&#xff09;Unit 5 test 简介 由电子科技大学组织开设&#xff0c;授课教师为李京南、庞慧、刘兆林等5位老师。 课程介绍 英语广泛用于工程技术领域的国际交流。如何使用简洁的语言清楚地传递信息是工程师在国际舞台上常常面临的问题。本课…

ARM S5PV210 串行通信编程实战

一、串行通信编程实战1 1、整个程序流程分析 (1) 整个串口通信相关程序包含 2 部分&#xff1a;uart_init 负责初始化串口&#xff0c;uart_putc 负责发送一个字节。 2、串口控制器初始化关键步骤 (1) 初始化串口的 Tx 和 Rx 引脚所对应的GPIO&#xff08;查原理图可知 Rx 和…

【数列分段DP】膜拜

P1564 膜拜 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意&#xff1a; 思路&#xff1a; 这是个经典模型&#xff1a;数列分段DP&#xff0c;在其他地方也出现过&#xff1a;(150条消息) 代码源每日一题div1 DP 数组划分_lamentropetion的博客-CSDN博客 这类DP模型核…

_13LeetCode代码随想录算法训练营第十三天-C++二叉树

_13LeetCode代码随想录算法训练营第十三天-C二叉树 题目列表 102.二叉树的层序遍历107.二叉树的层次遍历II199.二叉树的右视图637.二叉树的层平均值429.N叉树的层序遍历515.在每个树行中找最大值116.填充每个节点的下一个右侧节点指针117.填充每个节点的下一个右侧节点指针II…

说话人识别中的数据预处理和数据增强

数据预处理 假设已经采集到一些数据&#xff0c;在进行训练之前&#xff0c;需要先对数据做以下预处理&#xff1a; 数据清洗语音检测&#xff08;Voice Activity Detection&#xff0c;VAD&#xff0c;也叫Speech Detection&#xff0c;或Silence Suppression&#xff0c;静音…

ICMP V6(计算机网络-网络层)

IPv6 使用的 ICMP IETF 制定的与IPv6配套使用的ICMP新版本&#xff0c;即ICMPv6 ICMPv6报文作为IPv6分组有效载荷进行传输&#xff0c;对应的IPv6“下一个首部字段”的值为58 ICMPv6 的报文格式和 IPv4 使用的 ICMP 的相似&#xff0c;即前 4 个字节的字段名称都是一样的&…

Linux下进程及其相关概念理解

目录 何为进程&#xff1f; task_struct 中存储了什么进程信息&#xff1f; 如何查看进程&#xff1f; 如何获取进程pid&#xff1f; 如何创建子进程&#xff1f; 为什么返回值如此呢&#xff1f; 为什么有两个返回值&#xff1f; 进程状态 进程的一般状态 运行态 终…

18-JavaSE基础巩固练习:正则表达式练习

正则表达式基本练习 一、练习1 1、需求 请编写正则表达式验证用户输入的手机号码是否满足要求。请编写正则表达式验证用户输入的邮箱号是否满足要求。请编写正则表达式验证用户输入的电话号码是否满足要求。 2、思路&#xff1a; 心得&#xff1a; 拿着一个正确的数据&…

SAP UI5 Smart Chart 功能介绍

笔者已经写过一篇详细的文章介绍 SAP UI5 Smart Chart 的使用方法&#xff1a; SAP UI5 应用开发教程之一百五十三 - 使用 SAP UI5 Smart Chart 控件轻松绘制十数种不同类型的专业图表 本文泛泛地介绍 Smart Chart 提供的一些其他功能。 工具栏右侧的按钮可用于选择图表类型…

Zookeeper 1 初识 Zookeeper 1.1 Zookeeper 概念

Zookeeper 【黑马程序员Zookeeper视频教程&#xff0c;快速入门zookeeper技术】 文章目录Zookeeper1 初识 Zookeeper1.1 Zookeeper 概念1.1.1 Zookeeper 概念1 初识 Zookeeper 1.1 Zookeeper 概念 1.1.1 Zookeeper 概念 Zookeeper 是 Apache Hadoop 项目下的一个子项目&…

3、前端笔记-JS-变量

1、什么是变量 变量是用于存放数据的容器&#xff0c;可以通过变量名获取数据 本质&#xff1a;变量是程序在内存中申请的一块用来存放数据的空间 2、变量的使用 2.1 声明变量和赋值 1、声明变量 2、给变量赋值 var:JS关键字&#xff0c;用来声明变量。使用这个关键字后&a…

(二)计算机组成原理——计算机的基本组成

目录 冯诺依曼计算机的特点 计算机硬件框图 系统复杂性管理的方法&#xff08;3’Y&#xff09; 计算机的工作步骤 上机前的准备 计算机的工作过程 存储器的基本组成 运算器的基本组成及操作过程 控制器 计算机组成原理课程笔记。 冯诺依曼计算机的特点 冯诺依曼计算…

sandbox启动未加载repeater的问题

背景 通过官方提供的 repeater 的下载链接&#xff0c;并不能够在sandbox启动时&#xff0c;加载进行&#xff0c;我们可以看下sandbox的日志截图 但是如果通过源码的repeater进行安装后&#xff0c;就能够成功加载到repeater。 分析 这是个很奇怪的问题&#xff0c;想要分析…

Sentinel的规则

四.Sentinel的规则 1.流控规则 1.1流控规则有哪些? 流量控制有以下几个角度: 资源的调用关系&#xff0c;例如资源的调用链路&#xff0c;资源和资源之间的关系&#xff1b;运行指标&#xff0c;例如 QPS&#xff08;每秒查询率&#xff09;、线程池、系统负载等&#xff…

深度学习时数据量过大的一个解决思路:将零散的数据集中化

问题描述 最近笔者在做一个kaggle上的树叶分类的题目&#xff08;https://www.kaggle.com/competitions/classify-leaves&#xff09;&#xff0c;这个题目要求根据一张树叶的图片给出这片树叶的类别&#xff0c;这个题目也是沐神的《动手深度学习》课程里的一个课程竞赛题目。…