【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. 双向链表核心优势:快速取消中断节点、回溯校验前驱、队列容错健壮。
相关推荐
踏着七彩祥云的小丑1 小时前
Go 学习第6天:结构体 + 切片 + range遍历
开发语言·学习·golang·go
读书札记20221 小时前
Qt中windeployqt.exe工具的使用:解决使用CMake创建的项目点击exe文件后系统提示0xc000007b的问题
开发语言·qt
地铁潜行者1 小时前
消息堆积后,为什么一扩容消费者,MySQL 先被打崩了?
java·后端
地铁潜行者1 小时前
订单状态更新成功了,分账消息却没发出去:聊聊本地消息表的一致性坑
java·后端
亦暖筑序1 小时前
Java 8老系统SQL Agent实战:AI生成候选SQL,安全引擎拦截后再执行
java·人工智能·sql
CodeStats1 小时前
《源纹天书》卷一:归元初醒(第1-5章)
java
大囚长1 小时前
大模型服务端如何命中缓存
java·人工智能·缓存·dubbo
摇滚侠1 小时前
SpringMVC 入门到实战 拦截器 78-82
java·后端·spring·maven·intellij-idea
xiaoshuaishuai81 小时前
C# 定制化Markdown编辑器
开发语言·c#·编辑器