AQS源码解析

今天清明节放假正好趁此总结一下AQS的源码分析😊😊😊

AbstractQueuedSynchronizer 的简称,Doug Lea大师编写的AQS源码,其实就是对CAS的封装和丰富,用于实现同步器(如锁和信号量等)的基础框架

其实就是一个通用的同步框架,设置这样一个框架要满足以下几点:

  • 通用性:在实现底层必要的同步机制后,给上层调用留有空间,也就是保持对外接口的简单性和纯粹性
  • 利用CAS,原子的修改共享标志位
  • 等待队列:让队列的头部进行自旋获取标志位,而其他的线程则挂起

AQS的一个成员变量state,就是来判断共享资源是否被占用的标志位,设置为int类型是为了记录重入的次数,还有就是共享模式下表示线程的数量

然后它还有一个表示队列头尾的head、tail指针,这两个指针维护了一个FIFO的双向链表来对线程进行管理

java 复制代码
 1 private transient volatile Node head;
 2 private transient volatile Node tail;

队列中的每一个节点Node其实就是对线程的封装,其中也包括四种线程在队列中的等待状态,然后Node中方法也很简单,其中有一个就是获取前置Node

队列中的节点有两种模式,共享和独占,我说一下独占把,ReentrantLock底层就是基于独占模式的AQS来实现的

我们设计这样一个框架其实有两种主要的使用场景:

  • 尝试获得锁,不管有没有获得立即返回
  • 必须获得锁,如果当前锁被占用,则进行等待

源码中的tyrAcquire和acquire正好对应了这两种场景:

tyrAcquire在AQS中是直接抛出异常,是为了让使用的上层自己实现,因为在尝试获得锁的过程中可能包含业务自定义的逻辑,比如是否可重入

acquire方法是重要的,acquireQueued(addWaiter(Node.EXCLUSIVE),arg)

addWaiter(Node.EXCLUSIVE),arg方法是将当前线程封装为一个Node,然后加入等待队列,因为是符合FIFO的,所以通过CAS操作将Node添加到队尾

这里它采用了快速插入的机制,就是一开始先假设并发量比较少(就是前置节点不为空)的情况下CAS进行插入,如果插入失败,则执行enq(node);方法来循环尝试插入

在队列中插入成功后返回一个Node,接下来就是acquireQueued方法

它首先去拿当前节点的前置节点,如果前置节点为head,说明是队首然后当前节点有权限来尝试获得锁,如果不是队首,然后看当前线程是否需要挂起等待,如果需要就挂起等待,如果不需要则继续循环判断是否有权限来尝试获得锁

当前线程是否需要挂起等待的逻辑:

它判断它的前置节点是否也在等待获得锁,如果是,则当前这个线程就挂起等待,如果当前线程是取消状态,则从队列中取消该节点

如果判断当前节点需要被挂起,则执行下面方法:

就是将当前线程挂起阻塞在这,一直等到持有锁的线程来调用relese方法来唤醒

大量线程挂起,然后得有relese释放,来看一下relese的释放:

和tryAcquire一样,它的tryRelese也是交给上层去实现

主要是relese方法,来看unparkSuccessor方法是怎么释放的:

它是从队尾依次向前搜索,找到head后面最靠前的并且是非CANCELED的节点来唤醒,调用unpark

从后往前搜索是为了避免在多线程下新建Node节点时没有与后面节点建立Next连接导致搜索中断

相关推荐
计算机毕业设计指导17 分钟前
基于Spring Boot + Vue 3的社区养老系统设计与实现
vue.js·spring boot·后端
拾忆,想起17 分钟前
Redisson 分布式锁的实现原理
java·开发语言·分布式·后端·性能优化·wpf
几颗流星18 分钟前
Rust 常用语法速记 - 解构赋值
后端·rust
我想当数字游民23 分钟前
Go的切片是什么?一些小细节和容易错的地方
后端·golang
肖焱25 分钟前
Java中的集合类有哪些?如何分类的?
后端
野生程序员y27 分钟前
Spring DI/IOC核心原理详解
java·后端·spring
是萝卜干呀1 小时前
IIS 部署 asp.net core 项目时,出现500.19、500.31问题的解决方案
后端·iis·asp.net·hosting bundle
从零开始学习人工智能1 小时前
SpringBoot + Apache Tika:一站式解决文件数据提取难题
spring boot·后端·apache
IT_陈寒1 小时前
Python 3.12 的这5个新特性,让我的代码性能提升了40%!
前端·人工智能·后端
华仔啊1 小时前
别再被 Stream.toMap() 劝退了!3 个真实避坑案例,建议收藏
javascript·后端