AQS源码浅析
欢迎大家访问handsomecui的博客,转载请注明地址https://www.cnblogs.com/handsomecui,欢迎加入技术群讨论:778757421
一、AQS简介
AbstractQueuedSynchronizer 抽象队列同步器。简称AQS,同时拥有 同步队列 与 等待队列
二、源码浅析
- 同步队列
线程调用了lock 方法,首先调用 acquire 方法请求锁,acquire 里面首先将现场节点封装Node 加入到同步队列的队尾,然后 在 acquireQueued 的循环里面,while 的判断 是不是head ,是则直接return 说明获取到锁,如果不是 则 调用了 park 方法阻塞,但是while 循环的跳出只能是 当前node 是head 。( 但是前面也说了 head 移除之后会 unpark 后面的结点,并且将后面的setHead) ,所以对于每个在抽象队列同步器的同步队列里面的Node,都是阻塞的,但是同步器每次acquire都会有一个acquireQueued ,相当于每个Node 都会 循环的请求,但是循环是同步器发出的,类似于一个“自旋”,这个自旋是 同步器控制的,线程还是阻塞的。
1 2 3 4 5 | public final void acquire( int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } |
2. 等待队列
调用 await 方法,进入condition的等待队列,释放锁,进入一个 while 循环,只有当线程回到同步队列或者被中断才可以跳出while,while里面 park 掉线程 ----- 在 condition调用 signal 之后,该condition上面的节点的 firstWaiter 首先加入到 同步队列尾部 , 会 unpark 线程,退出 while 之后,又会进入 acquireQueued 的循环里面,同样存在之前的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); int interruptMode = 0 ; while (!isOnSyncQueue(node)) { LockSupport.park( this ); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0 ) break ; } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null ) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0 ) reportInterruptAfterWait(interruptMode); } |
3. 总结
-
- 等待队列,是已经获取到锁的线程,需要用到其他线程的数据,主动调用 wait() 方法,并且释放锁,唤醒同步队列的后继节点(非公平不用唤醒),然后当前节点构造新的Node进入等待队列,当有其他 在同步队列里面 已经获得锁的线程调用 notify()(或者 signal())之后,才会重新加入同步队列,尝试获取锁。 等待队列的节点也调用了 park () 方法阻塞。
- Condition 接口中的 await() 与 signal() 函数分别用于 是线程进行等待状态---加入到等待队列中 、唤醒等待队列中的首节点的线程。
- 在ReetrantLock 里面,公平与非公平自旋锁,也是基于 同步器的,同步器的队列是默认阻塞的 ,ReetrantLock 里面是自旋非阻塞的。compareAndSetState
- ReetrantLock 里面 公平与非公平的自旋锁,都是在一直自旋没有 park(), 公平多了一个判断当前节点是否有前驱节点语句
1
if
(!hasQueuedPredecessors()...
三、工具类
- LockSupport
定义了一系列的公有静态方法,用于 阻塞&&唤醒 一个线程。
LockSupport 中的park 与 unpark 等方法,最终都是调用的 Unsafe 类中的方法,而 Unsafe 类中提供的是一系列的硬件级别的原子操作 ,有点类似于 汇编级别或者机器码级别 指令一样
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架