

Java并发编程:AQS抽象队列同步器 系统性知识体系
一、AQS概述与核心定位
AbstractQueuedSynchronizer(抽象队列同步器)是Java并发包java.util.concurrent.locks的基石,由Doug Lea设计。它提供了一个基于FIFO等待队列的同步器框架,用于实现依赖于原子状态的锁和同步器。
1.1 核心设计思想
- 状态驱动:用一个
volatile int state变量表示同步状态 - 队列管理:内置双向链表实现的CLH变种队列管理等待线程
- 模板方法模式:AQS实现了同步队列的核心逻辑,子类通过重写
protected方法定义具体的同步语义 - 两种模式:支持独占模式(Exclusive)和共享模式(Shared)
1.2 AQS在JUC中的地位
几乎所有JUC同步工具都直接或间接基于AQS实现:
- 锁:
ReentrantLock、ReentrantReadWriteLock - 同步器:
CountDownLatch、CyclicBarrier、Semaphore、Exchanger - 异步任务:
FutureTask、ThreadPoolExecutor中的Worker
二、AQS核心数据结构
2.1 同步状态state
private volatile int state;
- 核心变量:表示锁的持有状态或同步资源数量
- volatile修饰:保证多线程间的可见性
- 操作方法:
getState():原子获取当前状态setState(int newState):原子设置状态compareAndSetState(int expect, int update):CAS原子更新状态
2.2 等待节点Node
构成CLH队列的基本单元,每个等待线程被封装为一个Node节点:
| 属性 | 类型 | 说明 |
|---|---|---|
waitStatus |
int |
节点等待状态 |
prev |
Node |
前驱节点 |
next |
Node |
后继节点 |
thread |
Thread |
当前节点对应的线程 |
nextWaiter |
Node |
条件队列中的下一个节点 |
waitStatus枚举值:
SIGNAL(-1):后继节点需要被当前节点唤醒CANCELLED(1):节点已取消(超时或中断)CONDITION(-2):节点在条件队列中等待PROPAGATE(-3):共享模式下需要传播唤醒0:初始状态
2.3 CLH同步队列
AQS使用双向链表实现的CLH变种队列:
- head:头节点(哑节点,不代表实际等待线程)
- tail:尾节点,指向队列最后一个等待节点
- 特点:FIFO、无锁入队(CAS实现)、高效的节点管理
三、CLH队列核心机制
3.1 CLH队列的由来
原始CLH锁是Craig、Landin、Hagersten发明的自旋锁,AQS对其进行了改造:
- 将自旋改为阻塞+唤醒机制
- 从单向链表改为双向链表
- 增加了取消、条件等待等高级功能
3.2 入队操作
当线程获取锁失败时,会被封装成Node节点加入队列尾部:
- 创建新节点,设置
thread为当前线程 - CAS设置tail为新节点(保证原子性)
- 将原tail节点的
next指向新节点
3.3 出队操作
当锁被释放时,会唤醒头节点的后继节点:
- 被唤醒的线程尝试获取锁
- 获取成功后,将自己设置为新的head节点
- 原head节点的
next置为null,帮助GC回收
3.4 节点取消
当线程等待超时或被中断时,节点会被标记为CANCELLED:
- 将节点的
waitStatus设置为CANCELLED - 调整前驱和后继节点的指针,将取消节点从队列中移除
- 唤醒后继节点(如果需要)
四、独占模式(Exclusive)
独占模式下,同一时刻只能有一个线程持有锁。典型实现:ReentrantLock。
4.1 获取锁流程
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
详细步骤:
- 尝试获取锁:调用
tryAcquire(arg)(子类实现) - 入队:获取失败,调用
addWaiter(Node.EXCLUSIVE)创建独占节点并加入队列 - 队列中等待:调用
acquireQueued(node, arg)在队列中自旋等待- 检查前驱节点是否是head节点
- 如果是,再次尝试获取锁
- 成功则设置为新head,返回
- 失败则检查是否需要阻塞
- 需要则调用
LockSupport.park()阻塞线程 - 被唤醒后继续循环
- 处理中断:如果等待过程中被中断,调用
selfInterrupt()设置中断标志
4.2 释放锁流程
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
详细步骤:
- 尝试释放锁:调用
tryRelease(arg)(子类实现) - 唤醒后继:释放成功,调用
unparkSuccessor(head)唤醒head节点的后继节点 - 后继节点被唤醒后,继续在
acquireQueued中尝试获取锁
4.3 可中断与超时获取
- 可中断获取:
acquireInterruptibly(int arg)- 在阻塞过程中可以响应中断
- 抛出
InterruptedException
- 超时获取:
tryAcquireNanos(int arg, long nanosTimeout)- 在指定时间内尝试获取锁
- 超时返回
false
五、共享模式(Shared)
共享模式下,多个线程可以同时持有锁。典型实现:Semaphore、CountDownLatch、ReentrantReadWriteLock的读锁。
5.1 获取锁流程
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
详细步骤:
- 尝试获取共享锁:调用
tryAcquireShared(arg)(子类实现)- 返回值
<0:获取失败 - 返回值
=0:获取成功,但没有剩余许可 - 返回值
>0:获取成功,还有剩余许可
- 返回值
- 入队等待:获取失败,调用
doAcquireShared(arg)将线程加入队列并自旋等待 - 传播唤醒:获取成功后,调用
setHeadAndPropagate(node, r)设置新head并传播唤醒后续共享节点
5.2 释放锁流程
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
详细步骤:
- 尝试释放共享锁:调用
tryReleaseShared(arg)(子类实现) - 传播释放:释放成功,调用
doReleaseShared()唤醒后继节点 - 后继节点被唤醒后,继续尝试获取共享锁
5.3 传播唤醒机制
共享模式的核心特点是传播唤醒:
- 当一个共享线程获取锁后,如果还有剩余许可,会继续唤醒下一个共享节点
- 这保证了所有等待的共享线程都能被及时唤醒
- 由
setHeadAndPropagate和doReleaseShared两个方法共同实现
六、条件变量Condition
Condition接口替代了Object的wait/notify机制,提供了更精确的线程等待/唤醒控制。一个Lock可以对应多个Condition。
6.1 ConditionObject实现
AQS的内部类ConditionObject实现了Condition接口:
- 维护一个单向条件队列
- 条件队列与同步队列相互独立
- 线程调用
await()时会从同步队列转移到条件队列 - 线程调用
signal()时会从条件队列转移回同步队列
6.2 await()流程
- 将当前线程封装成Node节点加入条件队列
- 释放当前持有的锁
- 阻塞线程,等待被唤醒
- 被唤醒后,重新获取锁
- 返回并继续执行
6.3 signal()/signalAll()流程
- signal():将条件队列中的第一个节点转移到同步队列,并唤醒该线程
- signalAll():将条件队列中的所有节点转移到同步队列,并依次唤醒
七、基于AQS实现的核心组件
7.1 ReentrantLock(可重入锁)
- 模式:独占模式
- 特点:可重入、支持公平/非公平模式
- state含义:表示锁的重入次数(0表示未锁定)
- 公平与非公平区别:
- 非公平:直接CAS尝试获取锁,失败再入队(默认)
- 公平:先检查队列是否有等待线程,有则直接入队
7.2 ReentrantReadWriteLock(读写锁)
- 模式:读锁(共享)+ 写锁(独占)
- state含义:高16位表示读锁数量,低16位表示写锁数量
- 特性:
- 写锁可以降级为读锁
- 读锁不能升级为写锁
- 写锁与读锁互斥,读锁之间共享
7.3 CountDownLatch(倒计时门闩)
- 模式:共享模式
- 用途:一个线程等待多个线程完成
- state含义:表示需要等待的线程数量
- 核心方法:
countDown():将计数减1await():等待计数变为0
7.4 CyclicBarrier(循环屏障)
- 实现:基于
ReentrantLock和Condition - 用途:多个线程互相等待,直到所有线程都到达屏障点
- 特点:可以循环使用,支持屏障动作
- 与CountDownLatch区别:
- CountDownLatch是一次性的,CyclicBarrier可以循环使用
- CountDownLatch是一个线程等待多个线程,CyclicBarrier是多个线程互相等待
7.5 Semaphore(信号量)
- 模式:共享模式
- 用途:控制同时访问资源的线程数量
- state含义:表示可用的许可数量
- 核心方法:
acquire():获取一个许可release():释放一个许可
7.6 FutureTask(异步任务)
- 模式:独占模式
- 用途:表示异步计算的结果
- state含义:表示任务的执行状态(NEW、COMPLETING、NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTED)
- 核心方法:
get():等待计算完成并获取结果cancel():取消任务
八、AQS设计思想与性能分析
8.1 核心设计思想
- 模板方法模式:将不变的队列管理逻辑封装在AQS中,可变的同步语义留给子类实现
- 自旋+阻塞:先自旋尝试获取锁,失败再阻塞,减少上下文切换
- CAS操作:使用CAS实现无锁队列操作,避免重量级锁的开销
- 双向链表:高效的节点插入和删除操作,支持节点取消
- 分层唤醒:精确控制线程的唤醒时机,避免不必要的唤醒
8.2 性能优势
- 高并发:通过CAS和volatile实现无锁操作,减少竞争
- 低延迟:自旋机制减少了线程阻塞和唤醒的次数
- 可扩展性:可以方便地实现各种自定义同步器
- 公平性支持:可以选择公平或非公平模式
8.3 局限性
- 单一状态变量:只能有一个同步状态变量,对于复杂的同步语义可能不够
- 不支持优先级:队列是FIFO的,不支持线程优先级调度
- 不可重入的独占锁:AQS本身不直接支持不可重入的独占锁(需要子类实现)
九、自定义AQS同步器
9.1 必须重写的方法
根据同步模式,需要重写以下方法之一:
- 独占模式:
tryAcquire(int arg)和tryRelease(int arg) - 共享模式:
tryAcquireShared(int arg)和tryReleaseShared(int arg)
9.2 示例:简单的不可重入独占锁
public class SimpleLock extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
return compareAndSetState(0, 1);
}
@Override
protected boolean tryRelease(int arg) {
setState(0);
return true;
}
public void lock() {
acquire(1);
}
public void unlock() {
release(1);
}
}
十、常见面试问题与实战要点
10.1 高频面试问题
- AQS的核心原理是什么?
- CLH队列的工作原理是什么?
- 独占模式和共享模式的区别是什么?
- ReentrantLock的公平锁和非公平锁实现有什么区别?
- CountDownLatch和CyclicBarrier的区别是什么?
- AQS中的waitStatus有哪些值,分别代表什么?
- Condition的实现原理是什么?
- AQS为什么使用双向链表而不是单向链表?
- AQS中的head节点为什么是哑节点?
- 如何基于AQS实现一个自定义同步器?
10.2 实战最佳实践
- 优先使用JDK提供的同步组件,不要重复造轮子
- 合理选择公平锁和非公平锁(非公平锁性能更好)
- 始终在finally块中释放锁,避免死锁
- 使用Condition实现精确的线程等待/唤醒
- 注意锁的粒度,避免长时间持有锁
- 对于读多写少的场景,优先使用读写锁
Java并发编程-AQS抽象队列同步器 面试高频考点清单
说明:本清单按照面试考察频率分级(★★★★★ 必考,★★★★ 高频,★★★ 中频),每个考点提炼核心答案要点,适合突击复习和背诵。所有内容均来自JDK 8源码标准实现。
一、基础概念与核心定位(★★★★★)
| 考点 | 核心答案要点 |
|---|---|
| 1. AQS是什么?核心设计思想 | 定义:Doug Lea设计的java.util.concurrent.locks基石,基于FIFO队列的同步器框架四大核心思想: ① 状态驱动: volatile int state表示同步状态② 队列管理:CLH变种双向队列管理等待线程 ③ 模板方法:AQS实现队列逻辑,子类重写 protected方法定义语义④ 双模式:支持独占/共享两种同步模式 |
| 2. AQS在JUC中的地位 | 几乎所有JUC同步工具都直接/间接基于AQS实现: • 锁: ReentrantLock、ReentrantReadWriteLock• 同步器: CountDownLatch、CyclicBarrier、Semaphore• 异步: FutureTask、ThreadPoolExecutor.Worker |
二、核心数据结构(★★★★★)
| 考点 | 核心答案要点 |
|---|---|
1. 同步状态state |
• 核心变量:表示锁持有状态或同步资源数量 • volatile修饰:保证多线程可见性• 原子操作: getState()、setState()、compareAndSetState() |
2. Node节点核心属性 |
• waitStatus:节点等待状态• prev/next:双向链表前驱/后继指针• thread:当前节点封装的线程• nextWaiter:条件队列中的下一个节点 |
3. waitStatus5个枚举值 |
• SIGNAL(-1):后继节点需要被当前节点唤醒• CANCELLED(1):节点已取消(超时/中断)• CONDITION(-2):节点在条件队列中• PROPAGATE(-3):共享模式下需要传播唤醒• 0:初始状态 |
| 4. CLH同步队列结构 | • 双向链表实现的FIFO队列 • head:哑节点(不代表实际等待线程)• tail:队列尾节点• 无锁入队:通过CAS设置tail保证原子性 |
三、CLH队列核心机制(★★★★★)
| 考点 | 核心答案要点 |
|---|---|
| 1. AQS对原始CLH锁的改造 | 原始CLH是自旋锁(单向链表),AQS改造为: ① 自旋→阻塞+唤醒机制 ② 单向链表→双向链表 ③ 增加节点取消、条件等待等高级功能 |
| 2. 入队流程(获取锁失败) | ① 创建Node.EXCLUSIVE/SHARED节点② CAS自旋设置 tail为新节点③ 将原tail的 next指向新节点 |
| 3. 出队流程(锁释放后) | ① 唤醒head节点的后继节点 ② 被唤醒线程尝试获取锁 ③ 获取成功后将自己设为新head ④ 原head的 next置null帮助GC |
| 4. 节点取消流程 | 触发条件:线程等待超时或被中断 ① 将节点 waitStatus设为CANCELLED② 调整前后指针,将取消节点从队列移除 ③ 唤醒后继节点(如果需要) |
| 5. 为什么用双向链表? | • 支持O(1)时间复杂度删除任意节点(如取消节点) • 单向链表只能从头遍历,删除中间节点需要O(n)时间 |
| 6. head为什么是哑节点? | • 简化队列操作逻辑,避免频繁判断head是否为null • 统一处理第一个等待节点和后续节点的唤醒流程 |
四、独占模式(Exclusive)(★★★★★)
同一时刻只能有一个线程持有锁,典型实现:
ReentrantLock
| 考点 | 核心答案要点 |
|---|---|
1. acquire()完整流程(源码级) |
acquireQueued细节: • 只有前驱是head时才尝试获取锁 • 失败则调用 LockSupport.park()阻塞• 被唤醒后继续循环 |
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 1. 子类尝试获取锁
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 2. 入队+自旋等待
selfInterrupt(); // 3. 处理中断
}
| 考点 | 核心答案要点 |
|---|---|
2. release()完整流程(源码级) |
public final boolean release(int arg) {
if (tryRelease(arg)) {
// 1. 子类尝试释放锁
Node h=head;
if (h!=null && h.waitStatus!=0)
unparkSuccessor(h); // 2. 唤醒head后继
return true;
}
return false;
}
| 考点 | 核心答案要点 |
|---|---|
| 3. 可中断与超时获取 | • acquireInterruptibly():阻塞时响应中断,抛出InterruptedException• tryAcquireNanos(long):指定时间内获取失败返回false |
五、共享模式(Shared)(★★★★)
多个线程可同时持有锁,典型实现:
Semaphore、CountDownLatch、读锁
| 考点 | 核心答案要点 |
|---|---|
1. tryAcquireShared返回值含义 |
• <0:获取失败• =0:获取成功,但无剩余许可• >0:获取成功,还有剩余许可 |
| 2. 传播唤醒机制 | 核心特点:一个共享线程获取锁后,若还有剩余许可,会继续唤醒后续共享节点 实现:由 setHeadAndPropagate()和doReleaseShared()共同完成目的:保证所有等待的共享线程都能被及时唤醒 |
| 考点 | 核心答案要点 |
|---|---|
| 3. 独占vs共享模式核心区别 |
| 对比维度 | 独占模式 | 共享模式 |
|---|---|---|
| 同一时刻持有线程数 | 1个 | 多个 |
| 典型实现 | ReentrantLock、写锁 | Semaphore、CountDownLatch、读锁 |
| 核心方法 | acquire()/release() |
acquireShared()/releaseShared() |
| 唤醒机制 | 只唤醒一个后继 | 传播唤醒多个后继 |
六、条件变量Condition(★★★★)
替代
Object.wait/notify,支持一个Lock对应多个Condition,实现精确唤醒
| 考点 | 核心答案要点 |
|---|---|
| 1. Condition vs Object.wait/notify | • 一个Lock可对应多个Condition,实现分组唤醒 • 支持可中断等待和超时等待 • 避免了 notifyAll()导致的"惊群效应" |
2. await()流程 |
① 将线程封装为Node加入条件队列(单向) ② 释放当前持有的锁 ③ 调用 LockSupport.park()阻塞④ 被唤醒后转移到同步队列 ⑤ 重新获取锁后返回 |
3. signal()vssignalAll() |
• signal():将条件队列第一个节点转移到同步队列并唤醒• signalAll():将条件队列所有节点转移到同步队列并依次唤醒 |
七、基于AQS实现的核心组件(★★★★★)
7.1 ReentrantLock(可重入锁)
- 模式:独占模式
- state含义:锁的重入次数(0表示未锁定)
- 公平vs非公平:
- 非公平(默认):直接CAS尝试获取锁,失败再入队
- 公平:先检查队列是否有等待线程,有则直接入队
7.2 ReentrantReadWriteLock(读写锁)
- 模式:读锁(共享)+ 写锁(独占)
- state划分:高16位=读锁数量,低16位=写锁数量
- 特性:写锁可降级为读锁,读锁不可升级为写锁
7.3 CountDownLatch vs CyclicBarrier(必考对比)
| 对比维度 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 实现原理 | 直接基于AQS共享模式 | 基于ReentrantLock+Condition |
| 用途 | 一个线程等待多个线程完成 | 多个线程互相等待到屏障点 |
| state含义 | 需要等待的线程数量 | 还需到达屏障的线程数 |
| 可复用性 | 一次性(计数到0后无法重置) | 可循环使用(reset()方法) |
| 异常处理 | 不支持 | 支持屏障动作和异常处理 |
7.4 Semaphore(信号量)
- 模式:共享模式
- state含义:可用许可数量
- 用途:控制同时访问资源的最大线程数
7.5 FutureTask(异步任务)
- 模式:独占模式
- state含义:任务执行状态(NEW→COMPLETING→NORMAL/EXCEPTIONAL)
- 核心:
get()方法阻塞等待任务完成
八、设计思想与性能分析(★★★)
| 考点 | 核心答案要点 |
|---|---|
| 1. 核心设计模式 | • 模板方法模式:封装不变的队列逻辑,子类实现可变的同步语义 • 享元模式:Node节点复用线程对象 • 观察者模式:节点等待前驱节点唤醒 |
| 2. 性能优势 | • 高并发:CAS+volatile实现无锁队列操作 • 低延迟:先自旋后阻塞,减少上下文切换 • 可扩展性:轻松实现自定义同步器 |
| 3. 局限性 | • 只有一个state变量,复杂同步语义需要额外变量• 队列是FIFO,不支持线程优先级调度 • 不直接支持不可重入独占锁(需子类实现) |
九、自定义AQS同步器(★★★)
| 考点 | 核心答案要点 |
|---|---|
| 1. 必须重写的方法 | • 独占模式:tryAcquire(int)和tryRelease(int)• 共享模式: tryAcquireShared(int)和tryReleaseShared(int) |
| 2. 简单不可重入独占锁示例 |
public class SimpleLock extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
return compareAndSetState(0, 1);
}
@Override
protected boolean tryRelease(int arg) {
setState(0);
return true;
}
public void lock() {
acquire(1); }
public void unlock() {
release(1); }
}
十、面试答题技巧与实战易错点(★★★★)
10.1 面试答题通用思路
回答AQS问题时遵循"总-分-例"结构:
- 总述:一句话说明核心概念
- 分点:按核心组件/流程分点阐述(突出关键词)
- 举例:结合JDK中的具体实现说明
10.2 实战易错点
- 必须在
finally块中释放锁,否则会导致死锁 - 非公平锁性能优于公平锁(默认使用非公平)
Condition.await()必须在持有锁的情况下调用- 读多写少场景优先使用
ReentrantReadWriteLock
终极必背10题(覆盖90%以上AQS面试题)
- 简述AQS的核心原理和设计思想
- 详细说明AQS的CLH队列结构和工作机制
- 独占模式下
acquire()和release()的执行流程 - 共享模式的传播唤醒机制是什么?为什么需要?
- ReentrantLock的公平锁和非公平锁实现有什么区别?
- CountDownLatch和CyclicBarrier的区别是什么?
- AQS中的
waitStatus有哪些值?分别代表什么? - Condition的实现原理是什么?与Object.wait/notify有什么区别?
- AQS为什么使用双向链表?head节点为什么是哑节点?
- 如何基于AQS实现一个自定义同步器?
Java并发-AQS 一页纸速记版
考前3分钟突击专用 | 只保留必考核心关键词和高频对比表格 | 覆盖90%以上AQS面试题
一、核心定位(★★★★★)
- AQS:JUC锁机制基石,Doug Lea设计
- 四大核心思想:
volatile state驱动 + CLH双向队列 + 模板方法 + 独占/共享双模式 - JUC全家桶:ReentrantLock、读写锁、CountDownLatch、CyclicBarrier、Semaphore、FutureTask
二、核心数据结构(★★★★★)
1. 同步状态state
volatile int:锁状态/资源数量- 原子操作:
getState()、setState()、compareAndSetState()
2. Node节点waitStatus(必背)
| 值 | 含义 |
|---|---|
| -1 | SIGNAL:后继需要被唤醒 |
| 1 | CANCELLED:节点已取消 |
| -2 | CONDITION:在条件队列 |
| -3 | PROPAGATE:共享模式传播 |
| 0 | 初始状态 |
3. CLH队列
- 双向链表FIFO
- head:哑节点(不存实际线程)
- tail:队列尾节点(CAS入队)
三、CLH核心机制(★★★★★)
- 入队:创建Node → CAS设tail → 原tail.next指向新节点
- 出队:唤醒head后继 → 获取锁 → 设为新head → 原head置空
- 为什么双向链表:O(1)删除取消节点
- 为什么哑节点:简化队列操作逻辑
四、独占vs共享模式(★★★★★)
| 维度 | 独占模式 | 共享模式 |
|---|---|---|
| 同时持有线程 | 1个 | 多个 |
| 核心方法 | acquire()/release() |
acquireShared()/releaseShared() |
| 唤醒机制 | 只唤醒1个后继 | 传播唤醒多个后继 |
| 典型实现 | ReentrantLock、写锁 | Semaphore、CountDownLatch、读锁 |
独占模式核心流程
tryAcquire() → 失败→addWaiter()→acquireQueued()(自旋+阻塞)→ 中断则selfInterrupt()
共享模式核心
tryAcquireShared()返回值:<0失败,=0成功无剩余,>0成功有剩余- 传播唤醒:有剩余许可则继续唤醒后续共享节点
五、Condition条件变量(★★★★)
- 替代
Object.wait/notify,一个Lock对应多个Condition - await()流程:加入条件队列 → 释放锁 → 阻塞 → 转移到同步队列 → 重新获取锁
- signal():转移条件队列第一个节点到同步队列
- 优势:分组精确唤醒,避免惊群效应
六、基于AQS的核心组件(★★★★★)
1. ReentrantLock
- 独占模式,
state=重入次数 - 非公平(默认):直接CAS抢锁 → 失败入队
- 公平:先检查队列是否有等待 → 有则直接入队
2. 读写锁(ReentrantReadWriteLock)
- 读锁(共享)+ 写锁(独占)
state:高16位=读锁数,低16位=写锁数- 特性:写锁可降级,读锁不可升级
3. CountDownLatch vs CyclicBarrier(必考对比)
| 维度 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 实现 | 直接基于AQS共享 | 基于Lock+Condition |
| 用途 | 一个线程等多个 | 多个线程互相等 |
| 可复用 | 一次性 | 可循环(reset()) |
4. Semaphore
- 共享模式,
state=可用许可数 - 用途:控制并发访问上限
七、终极必背10题(考前默念)
- AQS核心原理和设计思想
- CLH队列结构和工作机制
- 独占模式
acquire()/release()流程 - 共享模式传播唤醒机制
- ReentrantLock公平vs非公平实现
- CountDownLatch vs CyclicBarrier
- AQS的5个
waitStatus含义 - Condition实现原理和优势
- 为什么用双向链表?为什么head是哑节点?
- 如何基于AQS实现自定义同步器
使用说明
- 考前快速扫一遍加粗关键词和所有表格
- 重点记忆带
★★★★★的必考内容 - 终极必背10题能脱口而出,AQS面试基本稳了