大家好,我是程序员二叉。
简介
AQS是JUC锁体系的基石,ReentrantLock、CountDownLatch、Semaphore底层全部依赖AQS实现,属于大厂面试高频压轴考点。本文完整讲解AQS定义、核心思想、底层结构、两种工作模式、排队唤醒流程、双向链表设计原因,逻辑闭环,适用于面试背诵。欢迎点赞关注收藏。
一、AQS是什么?核心设计思想
1. AQS定义
AQS全称 AbstractQueuedSynchronizer ,抽象队列同步器,是java.util.concurrent.locks包下基础抽象类,是JUC中所有锁、同步工具的底层骨架。
ReentrantLock、ReadWriteLock、CountDownLatch、Semaphore、CyclicBarrier全部基于AQS实现。
2. 核心设计思想
- 用一个volatile修饰的state整数代表同步状态,通过CAS原子修改状态实现竞争;
- FIFO双向阻塞队列存放等待线程,竞争失败的线程入队阻塞;
- 划分独占、共享两种资源获取模式,适配排他锁与共享锁场景;
- 模板方法模式:父类定义整体流程,子类重写少量try获取/释放状态的方法,自定义同步逻辑。
二、AQS底层两大核心结构:state状态 + 双向阻塞队列
1. state同步状态(volatile int)
- 是资源计数器,含义由子类自定义:
- ReentrantLock:0=无锁,>0=重入次数;
- CountDownLatch:初始值为等待计数,减到0唤醒所有线程;
- Semaphore:代表剩余许可数量。
- 被
volatile修饰,保证多线程可见性;依靠Unsafe的CAS实现无锁原子更新。
2. 双向阻塞队列
队列节点为静态内部类Node,每个Node封装:
- 绑定的Thread线程对象;
- waitStatus等待状态(取消、等待、唤醒、条件等待等);
prev前驱指针、next后继指针,构成双向链表;
- 队列虚拟头结点head、尾结点tail,无实际线程,仅用于占位简化头尾操作;
- 竞争锁失败的线程封装为Node,插入队尾;释放资源时从队头依次唤醒等待线程。
三、AQS独占模式、共享模式区别
1. 独占模式(Exclusive)
- 资源同一时间仅允许一个线程持有;
- 代表实现:ReentrantLock、WriteLock写锁;
- 获取方法:
tryAcquire(),释放:tryRelease(); - 唤醒逻辑:释放后只唤醒后继一个线程,其余继续阻塞。
2. 共享模式(Share)
- 资源可同时被多个线程持有;
- 代表实现:Semaphore、CountDownLatch、ReadLock读锁;
- 获取方法:
tryAcquireShared()(返回int,>0代表还有剩余资源),释放:tryReleaseShared(); - 唤醒逻辑:释放后会传播唤醒,持续唤醒后续可拿到资源的所有共享线程。
四、AQS线程排队、唤醒完整流程
1. 线程排队入队流程
- 线程调用acquire获取同步资源,先执行子类
tryAcquire尝试拿锁; - 获取失败,调用
addWaiter把当前线程封装成Node; - 快速尝试CAS插入队尾,失败则进入
enq自旋循环,直到成功插入双向队列尾部; - 插入完成后执行
acquireQueued:自旋判断自身是否是头结点后继,尝试再次拿锁; - 拿锁依旧失败,校验前驱节点状态,安全前提下调用
LockSupport.park()阻塞线程。
2. 资源释放、线程唤醒流程
- 持有线程调用release释放资源,执行子类
tryRelease修改state状态; - 释放成功,找到head头结点;
- 找到head有效后继节点(过滤取消状态的节点);
- 使用
LockSupport.unpark()唤醒后继节点绑定的线程; - 被唤醒线程回到自旋逻辑,再次尝试竞争state资源;竞争成功后把自身设置为新head,旧head断开GC回收。
五、AQS阻塞队列为什么要用双向链表?
- 支持从后向前快速取消节点
线程等待过程中可中断取消,单向链表删除中间节点必须从头遍历;双向链表依靠prev前驱指针,能直接快速定位、断开自身,效率极高。 - 处理节点状态、安全校验
判断是否可以安全park阻塞时,需要校验前驱节点waitStatus状态,prev指针可以直接拿到前驱,无需从头遍历队列。 - 头尾操作更稳定
入队自旋时tail尾指针CAS失败,依靠双向指针稳定重试插入;唤醒时从head向后遍历,双向结构保证指针不会断裂、丢失节点。 - 容错性更强
单向链表一旦某个节点next指针丢失,后续全部节点失联;双向链表任意节点可通过prev回溯修复链路,队列结构更健壮。
面试速记总结
- AQS是JUC同步器底层模板,state+双向队列是两大基石;
- 模板方法:父类管排队阻塞,子类重写tryAcquire/tryRelease自定义逻辑;
- 独占单线程持有,共享多线程并发持有;
- 竞争失败入队park阻塞,释放unpark唤醒队头后继;
- 双向链表核心优势:快速取消中断节点、回溯校验前驱、队列容错健壮。