Java——详解ReentrantLock与AQS的关联以及AQS的数据结构和同步状态State
前言
Java中大部分同步类(Lock、Semaphore、ReentrantLock等)都是基于AbstractQueuedSynchronizer(简称为 AQS)实现的。
AQS 是一种提供了原子式管理同步状态、阻塞和唤醒线程功能以及队列模型的简单框架。
本文会先介绍应用层,再逐渐深入介绍原理层。通过介绍ReentrantLock 的基本特性和 ReentrantLock 与 AQS 的关联,来深入解读 AQS 相关独占锁的知识点。
本篇文章主要阐述 AQS 中独占锁的逻辑和 Sync Queue, 不包含共享锁和 Condition Queue 的部分(本篇文章核心为 AQS 原理剖析,只是简单介绍了 ReentrantLock,感兴趣同学可以阅读一下 ReentrantLock 的源码)。
下面列出本篇文章的大纲和思路,以便于大家更好地理解:
ReentrantLock
ReentrantLock 与synchronized的区别和使用
ReentrantLock 意思为可重入锁,指的是一个线程能够对一个临界资源重复加锁。为了帮助大家更好地理解 ReentrantLock 的特性,我们先将 ReentrantLock 跟常用的 Synchronized 进行比较,其特性如下(蓝色部分为本篇文章主要剖析的点):
ReentrantLock | Synchronized | |
---|---|---|
锁实现机制 | 依赖AQS | 监视器模式(monitor) |
灵活性 | 支持响应中断、超时、尝试获取锁 | 不灵活 |
锁释放方式 | 必须显式调用unloc() | 自动释放 |
锁类型 | 公平锁&非公平锁 | 非公平锁 |
条件队列 | 可关联多个条件队列 | 关联一个 |
可重入性 | 可重入 | 可重入 |
ReentrantLock:
public void test () throw Exception {// 1. 初始化选择公平锁、非公平锁ReentrantLock lock = new ReentrantLock(true);// 2. 可用于代码块lock.lock();try {try {// 3. 支持多种加锁方式,比较灵活 ; 具有可重入特性if(lock.tryLock(100, TimeUnit.MILLISECONDS)){ }} finally {// 4. 手动释放锁lock.unlock()}} finally {lock.unlock();}
}
Synchronized:
// **************************Synchronized 的使用方式
**************************
// 1. 用于代码块
synchronized (this) {}
// 2. 用于对象
synchronized (object) {}
// 3. 用于方法
public synchronized void test () {}
// 4. 可重入
for (int i = 0; i < 100; i++) {synchronized (this) {}
}
// **************************ReentrantLock 的使用方式
**************************
ReentrantLock 与 AQS 的关联
我们知道ReentrantLock 支持公平锁和非公平锁,并且 ReentrantLock的底层就是由 AQS 来实现的。那么 ReentrantLock 是如何利用AQS实现公平锁和非公平锁呢? 我们着重从这两者的加锁过程来理解一下它们与 AQS 之间的关系(加锁过程中与 AQS 的关联比较明显,解锁流程后续会介绍)
非公平锁源码中的加锁流程如下:
// java.util.concurrent.locks.ReentrantLock#NonfairSync
// 非公平锁
static final class NonfairSync extends Sync {...final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}...
}
上述代码解释如下:
- 若通过 CAS 设置变量 State(同步状态)成功,也就是获取锁成功,则将当前线程设置为独占线程。
- 若通过 CAS 设置变量 State(同步状态)失败,也就是获取锁失败,则进入Acquire 方法进行后续处理。
但第二步获取锁失败后,后续的处理策略是怎么样的呢?这块可能会有以问题:
- 某个线程获取锁失败的后续流程是什么呢?有以下两种可能:
- 将当前线程获锁结果设置为失败,获取锁流程结束。
这种设计会极大降低系统的并发度,并不满足我们实际的需求。所以就需要下面这种流程,也就是AQS 框架的处理流程。
-
存在某种排队等候机制,线程继续等待,仍然保留获取锁的可能,获取锁流程仍在继续。
-
对于问题 1 的第二种情况,既然说到了排队等候机制,那么就一定会有某种队列结构。
- 这种队列是什么数据结构呢?
- 处于排队等候机制中的线程,什么时候可以有机会获取锁呢?
- 如果处于排队等候机制中的线程一直无法获取锁,还是需要一直等待吗,还是有别的策略来解决这一问题?
带着非公平锁的这些问题,再看下公平锁源码中获锁的方式:
// java.util.concurrent.locks.ReentrantLock#FairSync
static final class FairSync extends Sync {... final void lock() {acquire(1);}...
}
对这块代码,我们可能会存在这种疑问:Lock 函数通过 Acquire 方法进行加锁,但是具体是如何加锁的呢?
结合公平锁和非公平锁的加锁流程,虽然流程上有一定的不同,但是都调用了Acquire 方法,而 Acquire 方法是 FairSync 和 UnfairSync 的父类 AQS 中的核心方法。
对于上边提到的问题,其实在 ReentrantLock 类源码中都无法解答,而这些问题的答案都在 Acquire 方法所在的类 AbstractQueuedSynchronizer 中,也就是本文的核心——AQS。下面我们会对 AQS 以及ReentrantLock 和 AQS 的关联做详细介绍。
AQS
首先,简单描述下 AQS 框架总的来说,AQS 框架共分为五层:
API层, 锁获取方法层,队列方法层,排队方法层,数据提供层
当有自定义同步器接入时,只需重写第一层所需要的部分方法即可,不需要关注底层具体的实现流程。
当自定义同步器进行加锁或者解锁操作时,先经过第一层的 API 进入 AQS 内部方法,然后经过第二层进行锁的获取,接着对于获取锁失败的流程,进入第三层和第四层的等待队列处理,而这些处理方式均依赖于第五层的基础数据提供层。
下面我们会从整体到细节,从流程到方法逐一剖析 AQS 框架,主要分析过程
如下:
原理概览
AQS 核心思想是:
- 如果被请求的共享资源是空闲状态,那么就将当前请求资源的线程设置为有效的工作线程,将共享资源设置为锁定状态;
- 如果共享资源被占用了,就需要一定的阻塞等待唤醒机制来保证锁分配。
这个机制主要用的是 CLH 队列的变体实现的,将暂时获取不到锁的线程加入到队列中。
CLH:Craig、Landin and Hagersten 队列,是单向链表,AQS 中的队列是CLH 变体的虚拟双向队列(FIFO)。
AQS 是通过将每条请求共享资源的线程封装成一个节点来实现锁的分配。
主要原理图如下:
AQS 使用一个** Volatile 的 int 类型的成员变量来表示同步状态**,通过内置的FIFO 队列来完成资源获取的排队工作,通过 CAS 完成对 State 值的修改。
AQS 数据结构
这里先看下 AQS 中最基本的数据结构——Node,Node 即为上面 CLH 变体队列中的节点。
方法和属性值 | 含义 |
---|---|
waitStatus | 当前节点在队列中的状态 |
thread | 表示处于该节点的线程 |
prev | 前驱指针 |
predecessor | 返回前驱节点,没有的话抛出 npe |
nextWaiter | 指向下一个处于 CONDITION 状态的节点(由于本文不讲述 Condition Queue 队列,这个里不多介绍) |
next | 后继指针 |
线程获取/等待的锁的两种模式:
模式 | 含义 |
---|---|
SHARED | 共享锁:表示线程以共享的模式等待锁 |
EXCLUSIVE | 独占锁:表示线程正在以独占的方式等待锁 |
waitStatus 有下面几个枚举值:
枚举 | 含义 |
---|---|
0 | 当一个 Node 被初始化的时候的默认值 |
CANCELLED | 为 1,表示线程获取锁的请求已经取消了 |
CONDITION | 为 -2,表示节点在等待队列中,节点线程等待唤醒 |
PROPAGATE | 为 -3,当前线程处在 SHARED 情况下,该字段才会使用 |
SIGNAL | 为 -1,表示线程已经准备好了,就等资源释放了 |
同步状态 State
在了解数据结构后,接下来了解一下 AQS 的同步状态——State。
AQS 中维护了一个** state 的字段,意为同步状态,是由 Volatile 修饰的,用于展示当前临界资源的获锁情况**。
// java.util.concurrent.locks.AbstractQueuedSynchronizer
private volatile int state;
下面提供了几个访问这个字段的方法:
方法名 | 描述 |
---|---|
protected final int getState() | 获取 State 的值 |
protected final void setState(int newState) | 设置 State 的值 |
protected final boolean compareAndSetState(int expect, int update) | 使用 CAS 方式更新 State |
这几个方法都是 Final 修饰的,说明子类中无法重写它们。
我们可以通过修改 State 字段表示的同步状态来实现多线程的独占模式和共享模式(加锁过程):
独占模式:
共享模式:
如果我们要自定义的同步工具,需要自定义获取同步状态和释放状态的方式,也就 是 AQS 架构图中的第一层:API 层。
参考了这里
相关文章:

Java——详解ReentrantLock与AQS的关联以及AQS的数据结构和同步状态State
前言 Java中大部分同步类(Lock、Semaphore、ReentrantLock等)都是基于AbstractQueuedSynchronizer(简称为 AQS)实现的。 AQS 是一种提供了原子式管理同步状态、阻塞和唤醒线程功能以及队列模型的简单框架。 本文会先介绍应用层&a…...

vue3+vite+ts 接入QQ登录
说明 前提资料准备 在QQ互联中心注册成为开发者 站点:https://connect.qq.com/创建应用,如图 js sdk方式 下载对应的sdk包 sdk下载:https://wiki.connect.qq.com/sdk%e4%b8%8b%e8%bd%bd 使用 下载离线js sdk 打开:https:…...

消息队列kafka及zookeeper机制
目录 一、zookeeper 1、zookeeper简介 2、zookeeper特点 3、zookeeper工作模式及机制 4、zookeeper应用场景及选举机制 5、zookeeper集群部署 ①实验环境 ②安装zookeeper 二、消息队列kafka 1、为什么要有消息队列 2、使用消息队列的好处 3、kafka简介 4、kafka…...

分布式 - 分布式体系架构:IT架构的演进过程
文章目录01. 应用与数据一体模式02. 应用服务和数据服务的分离03. 缓存与性能的提升04. 服务器集群处理并发05. 数据库读写分离06. 反向代理和 CDN07. 分布式文件系统和分布式数据库系统08. NoSQL和搜索引擎09. 业务拆分10. Redis缓存在应用服务器上是进程内缓存还是进程外缓存…...

CSDN 周赛42期
CSDN 周赛42期1、题目名称:鬼画符门之宗门大比2、题目名称:K皇把妹3、题目名称:影分身4、题目名称:开心的金明小结1、题目名称:鬼画符门之宗门大比 给定整数序列A。 求在整数序列A中连续权值最大的子序列的权值。 &…...

Vue:初识Vue
1、首先要导入vue.js <!-- 当你使用script标签安装vue之后,上下文就注册了一个全局变量vue --><script src"../1.Vue/js/vue.js"></script> 不能直接调用vue(),需要new vue(),否则会报错。 2、关于vue构造函数的参数opti…...

linux语言学习记录
文章目录前言一、linux文件结构二、指令三、Gvim编辑器1、命令模式2、底行命令四、正则表达式1、表达式匹配举例2、对文件里面内容进行操作3、使用 \( 和 )\ 符号括起正规表达式,即可在后面使用\1和\2等变量来访问和中的内容前言 记录自己学习linux的笔记ÿ…...

面向对象编程(进阶)7:面向对象特征三:多态性
一千个读者眼中有一千个哈姆雷特。 目录 7.1 多态的形式和体现 7.1.1 对象的多态性 举例: 7.1.2 多态的理解 7.1.3 举例 1、方法内局部变量的赋值体现多态 2、方法的形参声明体现多态 3、方法返回值类型体现多态 7.2 为什么需要多态性(polymorphism)&#x…...

vue尚品汇商城项目-day04【29.加入购物车操作(难点)】
文章目录29.加入购物车操作(难点)29.1加入购物车按钮29.2addCartSuce29.3购物车29.3.1 向服务器发送ajax请求,获取购物车数据29.3.2UUID临时游客身份29.3.3动态展示购物车29.4修改购物车产品的数量(需要发请求:参数理解…...

KubeSphere 社区双周报 | 4.8 深圳站 Meetup 火热报名中 | 2023.3.17-3.30
KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者,并对近期重要的 PR 进行解析,同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为:2023.03.17-2023.…...

ChatGPT热炒之前 搜索引擎SEO算法已经悄然改变
2022年4月起,某度算法有了新的调整,这对于靠SEO获得流量的公司简直可以说是灭顶之灾。原本SEO从业者还指望跟之前一样,等算法调整稳定后,网站的自然排名还会再回来,但等到了10月份,仍然没有回暖的迹象&…...

【Linux】Mysql之视图的基本操作
一、什么是视图 MySQL 视图(View)是一种虚拟存在的表,同真实表一样,视图也由列和行构成, 但视图并不实际存在于数据库中。行和列的数据来自于定义视图的查询中所使用的 表,并且还是在使用视图时动态生成的。…...

《扬帆优配》西藏地震!美史上最严排放新规将出台,美股收涨
当地时间周四,美股遍及收高,科技股领涨。因耶稣受难日,美股4月7日(周五)休市,周四为美股本周最终一个买卖日,从本周状况来看,纳指与标普500指数均录得跌幅,别离跌1.1%和0…...

Python 小型项目大全 66~70
六十六、简单替换密码 原文:http://inventwithpython.com/bigbookpython/project66.html 简单替换密码用一个字母代替另一个字母。由于字母A有 26 种可能的替换,B有 25 种可能的替换,C有 24 种可能的替换,等等,所以可能…...

Barra模型因子的构建及应用系列八之Earning_Yeild因子
一、摘要 在前期的Barra模型系列文章中,我们构建了Size因子、Beta因子、Momentum因子、Residual Volatility因子、NonLinear Size因子、Book-to-Price因子和Liquidity因子,并分别创建了对应的单因子策略,其中Size因子和NonLinear Siz因子具有…...

2022蓝桥杯省赛——卡片
问题描述 小蓝有 k 种卡片, 一个班有 n 位同学, 小蓝给每位同学发了两张卡片, 一位同学的两张卡片可能是同一种, 也可能是不同种, 两张卡片没有顺序。没有两位同学的卡片都是一样的。 给定 n, 请问小蓝的卡片至少有多少种? 输入格式 输入一行包含一个正整数表示 n 。 输出…...
数据结构-快速排序
一.概要 快速排序是一种基于分治思想的排序算法,其基本思路是选取一个基准值(pivot),通过一趟排序将待排序列分成两个部分,其中左半部分都小于基准值,右半部分都大于基准值,然后对左右两部分分…...
WuThreat身份安全云-TVD每日漏洞情报-2023-04-10
漏洞名称:Apple iOS/iPadOS 越界写入 漏洞级别:高危 漏洞编号:CVE-2023-28206 相关涉及:Apple iOS <16.4.0 漏洞状态:在野 参考链接:https://tvd.wuthreat.com/#/listDetail?TVD_IDTVD-2023-08810 漏洞名称:PHPGurukul Bank Locker Management System SQL 注入 漏洞级别:高…...

IDEA中查看源码点击Download Sources时出现Cannot download sources的问题复现及解决
IDEA中查看源码点击Download Sources时出现Cannot download sources的问题复现及解决 注意:实验环境的IDEA版本:2021.3.1 1、问题描述 1.1、当想看源码时,点击Download Sources 1.2、此时出现了Cannot download sources 2、解决办法 2.1、…...

R+VIC模型融合实践技术应用及未来气候变化模型预测/SWAT/HSPF/HEC-HMS
在气候变化问题日益严重的今天,水文模型在防洪规划,未来预测等方面发挥着不可替代的重要作用。目前,无论是工程实践或是科学研究中都存在很多著名的水文模型如SWAT/HSPF/HEC-HMS等。虽然,这些软件有各自的优点;但是&am…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...

OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...