【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. 双向链表核心优势:快速取消中断节点、回溯校验前驱、队列容错健壮。
相关推荐
周GZ4 小时前
简单讲解Java中静态方法与 实例方法
java·开发语言
疯狂打码的少年4 小时前
【数据结构】树的基本概念与二叉树定义
java·数据结构·笔记·算法
盗理者5 小时前
AI Agent 技能分享|SQL 性能诊断与优化
java·sql·spring·skill
GitLqr5 小时前
Java 26 终于原生支持 HTTP/3 了:告别 Netty,直接用 QUIC
java·netty·http3
用户40966601317515 小时前
Jackson 序列化:@JsonIgnore / @JsonProperty / @JsonFormat / @JsonInclude / @JsonUnwrapped 一次讲清楚
java·后端
码路漫漫5 小时前
用了 Caffeine,消息为什么还是被处理了两次?
java
云和数据.ChenGuang5 小时前
fastapi项目拆分实战数据模型
java·服务器·数据库·人工智能·深度学习·fastapi·强化学习
大白同学4215 小时前
初识Qt——创建第一个Qt项目
开发语言·qt
祈禾5 小时前
计算机组成原理之编译、汇编、链接、解释
开发语言·汇编·笔记·学习
吴声子夜歌5 小时前
正则指引——Ruby
开发语言·正则表达式·ruby