AQS(ReentrantLock)源码浅析

news/2025/10/3 18:47:13/文章来源:https://www.cnblogs.com/randolf/p/19124863

管程 — Java同步的设计思想

管程:指的是管理共享变量以及对共享变量的操作过程,让他们支持并发。
互斥:同一时刻只允许一个线程访问共享资源;
同步:线程之间如何通信、协作。

MESA模型

在管程的发展史上,先后出现过三种不同的管程模型,分别是Hasen模型、Hoare模型和MESA模型。现在正在广泛使用的是MESA模型。AQS便是对MESA的经典实现。

MEAS管程模型

管程中引入了条件变量的概念,而且每个条件变量都对应有一个等待队列。条件变量和等待队列的作用是解决线程之间的同步问题。

AQS原理分析

什么是AQS

java.util.concurrent包中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列、条件队列、独占获取、共享获取等,而这些行为的抽象就是基于AbstractQueuedSynchronizer(简称AQS)实现的,AQS是一个抽象同步框架,可以用来实现一个依赖状态的同步器。
JDK中提供的大多数的同步器如Lock, Latch, Barrier等,都是基于AQS框架来实现的

  • 一般是通过一个内部类Sync继承 AQS
  • 将同步器所有调用都映射到Sync对应的方法

AQS具备哪些特性?

  • 阻塞等待队列
  • 共享/独占
  • 公平/非公平
  • 可重入
  • 允许中断

AQS两种队列

  • 同步等待队列: 主要用于维护获取锁失败时入队的线程
  • 条件等待队列: 调用await()的时候会释放锁,然后线程会加入到条件队列,调用signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获得锁

AQS队列中的节点包含5种状态

  1. 值为0,初始化状态,表示当前节点在sync队列中,等待着获取锁。
  2. CANCELLED,值为1,表示当前的线程被取消(如因超时、中断等原因放弃等待锁);
  3. SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
  4. CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队中;
  5. PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;

ReentrantLock为独占锁,同一时刻只有一个线程能持有锁,释放锁时,只需唤醒同步队列中的下一个有效节点(通过 unparkSuccessor()),无需传播唤醒信号,因此不存在PROPAGATE。

同步等待队列

AQS当中的同步等待队列也称CLH队列,CLH队列是Craig、Landin、Hagersten三人发明的一种基于双向链表数据结构的队列,是FIFO先进先出线程等待队列,Java中的CLH队列是原CLH队列的一个变种,线程由原自旋机制改为阻塞机制。
AQS 依赖CLH同步队列来完成同步状态的管理:

  • 当前线程如果获取同步状态失败时,AQS则会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程
  • 当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。
  • 通过signal或signalAll将条件队列中的节点转移到同步队列。(由条件队列转化为同步队列

条件等待队列

AQS中条件队列是使用单向列表保存的,用nextWaiter来连接:

  • 调用await方法阻塞线程;
  • 当前线程存在于同步队列的头结点,调用await方法进行阻塞(从同步队列转化到条件队列
public class ConditionDemo {private static final ReentrantLock lock = new ReentrantLock();private static final Condition condition = lock.newCondition();public static void main(String[] args) throws InterruptedException {new Thread(() -> {lock.lock();try {// 让线程在condition上一直等待下去condition.await();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}, "t1").start();new Thread(() -> {lock.lock();try {// 让线程在condition上一直等待下去condition.await();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}, "t2").start();// 主线程两秒后执行Thread.sleep(2000);log.debug("准备获取锁,去唤醒 condition上阻塞的线程");lock.lock();try {// 唤醒condition上所有阻塞的线程condition.signalAll();log.debug("唤醒condition上阻塞的线程");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}

结合ReentrantLock浅析AQS源码

重入锁默认使用非公平锁,以下以公平锁为例解析流程

ReentrantLock核心源码流程
一、加锁

final void lock() {  //通过CAS设置state,成功则当前线程独占锁资源if (compareAndSetState(0, 1))  setExclusiveOwnerThread(Thread.currentThread());  else   acquire(1);  
}

二、尝试获取锁,失败则加入同步等待队列

public final void acquire(int arg) {  if (!tryAcquire(arg) &&  acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  selfInterrupt();  
}

三、公平锁:尝试获取锁。

protected final boolean tryAcquire(int acquires) {  final Thread current = Thread.currentThread();  int c = getState();  if (c == 0) {  //需要遵循先来后到,存在前继节点则不参与竞争if (!hasQueuedPredecessors() &&  compareAndSetState(0, acquires)) {  setExclusiveOwnerThread(current);  return true;        }  }  //当前线程独占锁,重入次数增加else if (current == getExclusiveOwnerThread()) {  int nextc = c + acquires;  if (nextc < 0)  throw new Error("Maximum lock count exceeded");  setState(nextc);  return true;    }  return false;  
}

四、尾插节点,以及节点状态变更逻辑

private Node addWaiter(Node mode) {  //当前线程node节点Node node = new Node(Thread.currentThread(), mode);  // Try the fast path of enq; backup to full enq on failure  Node pred = tail;  if (pred != null) {  //尾插到链表末端,并cas设置新的tailnode.prev = pred;  if (compareAndSetTail(pred, node)) {  pred.next = node;  return node;  }  }  //自旋+cas重复上面的尾插enq(node);  return node;  
}
//核心接口
final boolean acquireQueued(final Node node, int arg) {  boolean failed = true;  try {  boolean interrupted = false;  for (;;) {  final Node p = node.predecessor();  //node前继节点为头节点,则再次尝试获取锁if (p == head && tryAcquire(arg)) {//获取锁成功,设置node为头节点  setHead(node);  p.next = null; // help GC  failed = false;  return interrupted;  }  //检查更新前继节点状态(p.waitStatus),返回true阻塞当前nodeif (shouldParkAfterFailedAcquire(p, node) &&  parkAndCheckInterrupt())  interrupted = true;  }  } finally {  if (failed)  cancelAcquire(node);  }  
}//在整个acquire流程中,此方法是主要的控制waitStatus的地方private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;if (ws == Node.SIGNAL)/** This node has already set status asking a release* to signal it, so it can safely park.*/return true;if (ws > 0) {//跳过cancel状态的nodedo {node.prev = pred = pred.prev;} while (pred.waitStatus > 0);pred.next = node;} else {//前继节点为0或PROPAGATE,通过CAS设置为-1compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false;}

AQS中的核心关注点

公平和非公平锁,可重入锁是如何实现的

公平锁与非公平锁主要体现在锁竞争的机制上,下面对比两者的核心代码:
公平锁:

protected final boolean tryAcquire(int acquires) {  final Thread current = Thread.currentThread();  int c = getState();  if (c == 0) {  //需要遵循先来后到,存在前继节点则不参与竞争if (!hasQueuedPredecessors() &&  compareAndSetState(0, acquires)) {  setExclusiveOwnerThread(current);  return true;        }  }  //重入锁实现else if (current == getExclusiveOwnerThread()) {  //当前线程独占锁,重入次数增加int nextc = c + acquires;  if (nextc < 0)  throw new Error("Maximum lock count exceeded");  setState(nextc);  return true;    }  return false;  
}

非公平锁:

final boolean nonfairTryAcquire(int acquires) {  final Thread current = Thread.currentThread();  int c = getState();  if (c == 0) {//其他线程释放了锁,CAS设置state  if (compareAndSetState(0, acquires)) {  setExclusiveOwnerThread(current);  return true;        }  }  //重入锁实现else if (current == getExclusiveOwnerThread()) {  //当前线程独占锁,重入次数增加int nextc = c + acquires;  if (nextc < 0) // overflow  throw new Error("Maximum lock count exceeded");  setState(nextc);  return true;   } //获取锁失败 return false;  
}

区别很明显:公平锁竞争需要排队,而非公平锁可直接参与竞争
重入锁的实现:判断获取了锁的线程就是当前线程,则state+1,否则失败
注意:获取锁失败进入等待队列之后,公平锁与非公平锁是一样的

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

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

相关文章

做网站app价格多少钱网站首页布局风格

编程笔记 html5&css&js 017 HTML样式 一、HTML样式二、CSS3小结 HTML样式是用来控制网页元素外观的一组属性和值。 一、HTML样式 可以通过以下几种方式来为HTML元素添加样式&#xff1a; 内联样式&#xff1a;直接在HTML元素的style属性中添加样式。例如&#xff1a;…

完整教程:【数据结构】快速排序与归并排序的实现

完整教程:【数据结构】快速排序与归并排序的实现2025-10-03 18:42 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; displa…

05. 事件处理

一、信号与槽在 QML 中,信号与槽机制是对象间通信的一种重要方式。它允许对象在其状态改变或发生特定事件时通知其他对象,并触发相应的处理函数。信号 是对象发出的通知,表明某个事件已经发生。槽 (信号处理器)是…

网站流量分析的指标有哪些wordpress京东主题

文章目录 第6章 逻辑斯谛回归与最大熵模型6.1 逻辑斯谛回归模型6.1.1 逻辑斯谛分布6.1.2 二项逻辑斯谛回归模型6.1.3 模型参数估计6.1.4 多项逻辑斯谛回归 《统计学习方法&#xff1a;李航》笔记 从原理到实现&#xff08;基于python&#xff09;-- 第3章 k邻近邻法 《统计学习…

总结问题2 软工10.3

使用纯水机发生器公式进行不断的遍历, XN是上一个种子,它遍历出XN加1就是下一个种子。Jdk SIMULATOR outer point ln的方法中,它是利用了很多的重载,来实现不同的功能。关于4则运算,答题框的生成,可以以答题框为…

如何选择网站建设流程网站展示效果图

我想用来自Java的参数调用python程序。但是我的输出是空白。代码在这里。 Python代码在这里&#xff1a; import sys print(sys.argv[1]) Java代码在这里&#xff1a; public class PrintNumber{ public static void main(String[] args){ Process proc; try { proc Runtime.g…

BPL包无法调试的问题

转 由于系统结构是Host主程序动态加载BPL包的模式。所以用到了Package的调试,但无论如何有一个包就是无法调试(加断点不起作用)。经过N久的查找,发现: 1.包Package在编译,生成的时候会自动产生DCP和BPL文件,缺省…

学院网站群建设的目标网站开发背景和意义

国产大模型开源一哥再登场&#xff0c;最强双语LLM「全家桶」级开源&#xff01;340亿参数超越Llama2-70B 为什么说大模型训练很难&#xff1f; - 知乎 GitHub - jeinlee1991/chinese-llm-benchmark: 中文大模型能力评测榜单&#xff1a;覆盖百度文心一言、chatgpt、阿里通义千…

如何在国内做网站手机网站建设选 朗创营销

图为RUST吉祥物 大家好,我是get_local_info作者带剑书生,这里用一篇文章讲解get_local_info是怎样获得杀毒软件的病毒库时间的。 首先,先要了解get_local_info是什么? get_local_info是一个获取linux系统信息的rust三方库,并提供一些常用功能,目前版本0.2.4。详细介绍地址…

江苏省建设工程竣工备案网站wordpress发号系统

目录 1、环境 1.1 操作系统初始化配置 1.2 部署 docker引擎 1.3 部署 etcd 集群 1.4 准备签发证书环境 1.5 部署 Master 组件 1.6 部署 Worker Node 组件 1.7 部署 CNI 网络组件 1.7.1 部署 flannel 1.7.2 部署 Calico 1.7.3 node02 节点部署 1.7.4 部署 CoreDNS 1…

信息科学与数据分析:真正的区别是什么?

信息科学与数据分析:真正的区别是什么?pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "M…

awk命令一文速通

awk命令一文速通1. awk简介和基本语法格式 Awk自动地搜索输入文件,并把每一个输入行切分成字段。许多工作都是自动完成的,例如读取每个输入行、字段分割、存储管理、初始化等。在AWK中不需声明变量数据类型,它内置字…

小程序开发:开启定制化custom-tab-bar但不生效疑问,以及使用NutUI-React Taro的安装和使用

小程序开发:开启定制化custom-tab-bar但不生效疑问,以及使用NutUI-React Taro的安装和使用2025-10-03 18:28 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !import…

做影视外包的网站优化方案怎么写

长期佩戴耳机可能会出现听力下降、耳道感染等危害。 听力下降&#xff1a;长时间戴耳机可能会导致耳道内的声音过大&#xff0c;容易对耳膜造成一定的刺激&#xff0c;容易出现听力下降的情况。 耳道感染&#xff1a;长时间戴耳机&#xff0c;耳道长期处于封闭潮湿的情况下&a…

免费com域名网站湖北省勘察设计协会网站

我们新项目硬件设计上使用gpio口做按键&#xff0c;所以我就需要搞定这个驱动&#xff0c;本来想自己写一个gpio口的按键驱动&#xff0c;然后看了下内核下面的代码&#xff0c;已经有现成的了。Linux内核下游很多很多的现成驱动&#xff0c;只要你想得到的&#xff0c;基本都是…

获胜者网站建设旅游网页素材

目录 1、享元模式&#xff08;Flyweight Pattern&#xff09;含义 2、享元模式的UML图学习 3、享元模式的应用场景 4、享元模式的优缺点 5、C实现享元模式的简单实例 1、享元模式&#xff08;Flyweight Pattern&#xff09;含义 享元模式&#xff08;Flyweight&#xff09…

深圳网站建设 设计首选中国建设银行南京分行网站首页

儿童安全门和围栏 儿童安全门和围栏用于在门口&#xff08;如门道&#xff09;内设置围栏&#xff0c;或用作自支撑围栏&#xff0c;将幼儿可能在其中活动的区域围起来。这些商品可能由塑料、金属、乙烯树脂或木制组件等材料制成。此政策包括但不限于可扩展围栏、伸缩安全门和…

最短路练习

最短路为背景的题 + 做法是最短路的题A - Minimum Path https://www.luogu.com.cn/problem/CF1473E经典套路,发现可以把 max 和 min 换成任意路径中的边,然后 max 和 min 就会最小化这个式子 故写一个 Dijkstra + DP…

东莞网站建设选择菲凡网络wordpress布局切换功能

题目&#xff1a; P2024 [NOI2001] 食物链 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 本文学习自&#xff1a; 题解 P2024 【食物链】 - RE: 从零开始的异世界信竞生活 - 洛谷博客 (luogu.com.cn) ———— 关系并查集其实就是在普通并查集的基础上额外开个数组r…

依据XShell采用Git三板斧

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …