【JUC】AQS底层深度拆解|独占/共享模式|队列原理全详解

大家好,我是程序员二叉。


简介

AQS是JUC锁体系的基石,ReentrantLock、CountDownLatch、Semaphore底层全部依赖AQS实现,属于大厂面试高频压轴考点。本文完整讲解AQS定义、核心思想、底层结构、两种工作模式、排队唤醒流程、双向链表设计原因,逻辑闭环,适用于面试背诵。欢迎点赞关注收藏。


一、AQS是什么?核心设计思想

1. AQS定义

AQS全称 AbstractQueuedSynchronizer ,抽象队列同步器,是java.util.concurrent.locks包下基础抽象类,是JUC中所有锁、同步工具的底层骨架。

ReentrantLock、ReadWriteLock、CountDownLatch、Semaphore、CyclicBarrier全部基于AQS实现。

2. 核心设计思想

  1. 用一个volatile修饰的state整数代表同步状态,通过CAS原子修改状态实现竞争;
  2. FIFO双向阻塞队列存放等待线程,竞争失败的线程入队阻塞;
  3. 划分独占、共享两种资源获取模式,适配排他锁与共享锁场景;
  4. 模板方法模式:父类定义整体流程,子类重写少量try获取/释放状态的方法,自定义同步逻辑。

二、AQS底层两大核心结构:state状态 + 双向阻塞队列

1. state同步状态(volatile int)

  • 是资源计数器,含义由子类自定义:
    • ReentrantLock:0=无锁,>0=重入次数;
    • CountDownLatch:初始值为等待计数,减到0唤醒所有线程;
    • Semaphore:代表剩余许可数量。
  • volatile修饰,保证多线程可见性;依靠Unsafe的CAS实现无锁原子更新。

2. 双向阻塞队列

队列节点为静态内部类Node,每个Node封装:

  1. 绑定的Thread线程对象;
  2. waitStatus等待状态(取消、等待、唤醒、条件等待等);
  3. prev前驱指针、next后继指针,构成双向链表;
  • 队列虚拟头结点head、尾结点tail,无实际线程,仅用于占位简化头尾操作;
  • 竞争锁失败的线程封装为Node,插入队尾;释放资源时从队头依次唤醒等待线程。

三、AQS独占模式、共享模式区别

1. 独占模式(Exclusive)

  • 资源同一时间仅允许一个线程持有;
  • 代表实现:ReentrantLock、WriteLock写锁;
  • 获取方法:tryAcquire(),释放:tryRelease()
  • 唤醒逻辑:释放后只唤醒后继一个线程,其余继续阻塞。

2. 共享模式(Share)

  • 资源可同时被多个线程持有;
  • 代表实现:Semaphore、CountDownLatch、ReadLock读锁;
  • 获取方法:tryAcquireShared()(返回int,>0代表还有剩余资源),释放:tryReleaseShared()
  • 唤醒逻辑:释放后会传播唤醒,持续唤醒后续可拿到资源的所有共享线程。

四、AQS线程排队、唤醒完整流程

1. 线程排队入队流程

  1. 线程调用acquire获取同步资源,先执行子类tryAcquire尝试拿锁;
  2. 获取失败,调用addWaiter把当前线程封装成Node;
  3. 快速尝试CAS插入队尾,失败则进入enq自旋循环,直到成功插入双向队列尾部;
  4. 插入完成后执行acquireQueued:自旋判断自身是否是头结点后继,尝试再次拿锁;
  5. 拿锁依旧失败,校验前驱节点状态,安全前提下调用LockSupport.park()阻塞线程。

2. 资源释放、线程唤醒流程

  1. 持有线程调用release释放资源,执行子类tryRelease修改state状态;
  2. 释放成功,找到head头结点;
  3. 找到head有效后继节点(过滤取消状态的节点);
  4. 使用LockSupport.unpark()唤醒后继节点绑定的线程;
  5. 被唤醒线程回到自旋逻辑,再次尝试竞争state资源;竞争成功后把自身设置为新head,旧head断开GC回收。

五、AQS阻塞队列为什么要用双向链表?

  1. 支持从后向前快速取消节点
    线程等待过程中可中断取消,单向链表删除中间节点必须从头遍历;双向链表依靠prev前驱指针,能直接快速定位、断开自身,效率极高。
  2. 处理节点状态、安全校验
    判断是否可以安全park阻塞时,需要校验前驱节点waitStatus状态,prev指针可以直接拿到前驱,无需从头遍历队列。
  3. 头尾操作更稳定
    入队自旋时tail尾指针CAS失败,依靠双向指针稳定重试插入;唤醒时从head向后遍历,双向结构保证指针不会断裂、丢失节点。
  4. 容错性更强
    单向链表一旦某个节点next指针丢失,后续全部节点失联;双向链表任意节点可通过prev回溯修复链路,队列结构更健壮。

面试速记总结

  1. AQS是JUC同步器底层模板,state+双向队列是两大基石;
  2. 模板方法:父类管排队阻塞,子类重写tryAcquire/tryRelease自定义逻辑;
  3. 独占单线程持有,共享多线程并发持有;
  4. 竞争失败入队park阻塞,释放unpark唤醒队头后继;
  5. 双向链表核心优势:快速取消中断节点、回溯校验前驱、队列容错健壮。
相关推荐
小bo波18 小时前
Java Swing 图形用户界面实验 —— 从算术练习到游戏开发的完整实践
java·课程设计·gui·游戏开发·扫雷·swing
咖啡八杯19 小时前
GoF设计模式——备忘录模式
java·后端·spring·设计模式
HjhIron1 天前
面试常客:字符串算法从入门到进阶
算法·面试
大志说编程1 天前
Agent面试真题06: 十分钟带你快速掌握Agent记忆管理高频面试题(附详细答案)
后端·面试·ai编程
众人皆醒我独醉1 天前
Kubernetes 为什么不直接调度容器?非要套一层 Pod
面试
亮亮不想说话958881 天前
iOS底层探索 -- GCD分析
面试
程序员小假1 天前
从问题到答案:RAG系统完整处理流程与核心机制深度拆解
后端·面试·agent
SamDeepThinking1 天前
裁掉那个差程序员后,给你看团队里高手的代码:这个习惯,希望你有
java·后端·程序员
朕瞧着你甚好1 天前
技术雷达 & Java 集成评估报告 — Apache Tika 3.3.1
java·ai编程
沉默王二1 天前
阿里一面,我霸气反问:你说你们在做Agent项目,说说langchain、muti-agent、a2a这些你们都是怎么做的?面试官一直在擦汗。。
面试·agent·ai编程