【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. 双向链表核心优势:快速取消中断节点、回溯校验前驱、队列容错健壮。
相关推荐
程序喵大人13 小时前
【C++进阶】STL容器与迭代器 - 01 STL 容器先解决元素放在哪里
开发语言·c++·stl
cui_ruicheng13 小时前
Python从入门到实战(十六):多进程编程
开发语言·python
Jelena1577958579214 小时前
电商运营分析数据比价接口实战:多平台价格监控与智能决策系统
java·大数据·数据库
触底反弹14 小时前
🚀 从 DOM0 级到 React 合成事件:前端事件监听的 20 年演进史
前端·react.js·面试
kyriewen14 小时前
我给前端项目的接口请求套了6层保护——才发现以前一直在裸奔
前端·javascript·面试
wdfk_prog14 小时前
嵌入式面试真题第 15 题:不可恢复异常后的通用崩溃快照、调用栈保存与离线分析架构
linux·开发语言·面试·架构
晴空了无痕14 小时前
从 Go 基础到 K8s:一条可落地的 Go 服务端成长路线
开发语言·后端·golang·kubernetes
神明不懂浪漫15 小时前
【第五章】Java中的继承与多态
java·开发语言
神州世通15 小时前
解密企业通信安全防线:Avaya Aura 三层安全架构深度解析
开发语言·php
AC赳赳老秦16 小时前
企业工商公开信息采集分析:OpenClaw 批量查询企业工商信息,生成企业画像报告
大数据·开发语言·python·自动化·php·deepseek·openclaw