AQS(AbstractQueuedSynchronizer)是什么?⭐⭐⭐⭐⭐
面试频率:★★★★★ 工作频率:★★★★★
⚡30 秒速记(复习只看这里)
🟢 一句话
AQS 是 Java 并发包中的锁框架,通过一个 state 状态变量 + FIFO 等待队列实现线程同步。
核心:
state状态
+
等待队列
+
CAS🟡 AQS 解决什么问题?
synchronized:
synchronized(lock){
}JVM 自动管理:
加锁
解锁
等待
简单。
但是:
如果需要:
尝试获取锁
超时等待
公平锁
多个条件队列
synchronized 不够灵活。
所以 Java 提供:
Lock而:
ReentrantLock
↓
AQS📚 一、AQS 是什么?
AQS:
全称:
AbstractQueuedSynchronizer中文:
抽象队列同步器作用:
提供一个通用的锁实现框架。
很多并发工具底层都使用 AQS:
ReentrantLock
CountDownLatch
Semaphore
FutureTask二、AQS 核心结构
AQS:
AQS
|
|---- state
|
|---- 等待队列
|
|
ThreadA
|
ThreadB
|
ThreadC两个核心:
1. state 状态变量
private volatile int state;表示:
锁的状态。
例如:
state = 0
没有线程持有锁线程获取:
state = 1表示:
锁被占用2. FIFO 等待队列
FIFO:
Full Name:
First In First Out意思:
先进先出例如:
线程等待:
头
↓
线程B
↓
线程C
↓
尾B 先进入。
通常 B 先获得锁。
三、ReentrantLock 和 AQS 关系
代码:
ReentrantLock lock =
new ReentrantLock();底层:
ReentrantLock
↓
Sync
↓
AQS
↓
state + 队列所以:
ReentrantLock 本身不是直接实现锁。
它借助 AQS。
四、AQS 获取锁流程
假设:
当前:
state = 0线程A:
尝试获取:
CAS(0,1)成功:
state = 1线程A获得锁。
线程B:
也尝试:
CAS(0,1)失败。
原因:
当前:
state = 1怎么办?
进入等待队列:
AQS队列
线程B线程C:
继续:
线程B
↓
线程C五、为什么需要等待队列?
如果没有队列:
所有线程不停:
CAS
↓
失败
↓
CAS
↓
失败大量消耗 CPU。
类似:
CAS 自旋问题。
有队列:
抢锁失败
↓
进入等待
↓
等待唤醒效率更高。
六、FIFO 是什么意思?
FIFO:
First In First Out先进先出。
例如:
线程B先等待
↓
线程C后等待
队列:
B
↓
C释放锁后:
通常:
B先获取
↓
C后获取但是注意:
AQS 默认不是完全公平。
七、公平锁和非公平锁
1. 公平锁
按照等待顺序获取:
线程A
↓
线程B
↓
线程C先来的先获得。
创建:
new ReentrantLock(true)优点:
公平。
缺点:
性能低。
2. 非公平锁
谁抢到谁获得。
例如:
等待队列:
线程B
↓
线程C突然:
线程D来了。
可能:
线程D直接抢到锁B、C继续等待。
默认:
new ReentrantLock()是:
非公平锁八、为什么 ReentrantLock 叫可重入锁?
Reentrant:
意思:
可以重复进入例如:
线程A:
lock.lock();
methodA();methodA:
lock.lock();如果不可重入:
线程A会等待自己释放锁。
造成死锁。
可重入:
使用:
state计数例如:
第一次:
state=1第二次:
state=2释放两次:
state=0九、AQS 和 CAS 的关系
AQS 获取锁:
使用:
CAS修改:
state例如:
state:
0 → 1成功:
获得锁。
所以:
关系:
CAS
↓
修改state
↓
AQS
↓
ReentrantLock十、ReentrantLock 和 synchronized 区别
十一、为什么 ReentrantLock 必须 finally unlock?
代码:
lock.lock();
try {
doSomething();
} finally {
lock.unlock();
}原因:
ReentrantLock 是 Java 类。
JVM 不知道:
什么时候释放。
如果异常:
lock.lock();
doSomething(); //异常
lock.unlock();可能:
unlock 没执行。
导致:
锁一直占用。
所以:
必须:
finally保证释放。
十二、synchronized 为什么不用 unlock?
因为:
synchronized 是 JVM 关键字。
JVM 自动插入:
monitorenter
执行代码
monitorexit即使异常:
也会释放。
十三、工作场景
例如:
库存扣减:
lock.lock();
try {
stock--;
} finally {
lock.unlock();
}保证:
多个线程不会同时修改库存。
🎤 面试回答(30秒)
AQS 是 Java 并发包中的同步框架,核心是一个 volatile 的 state 状态变量和 FIFO 等待队列。ReentrantLock 底层基于 AQS,通过 CAS 修改 state 来竞争锁,竞争失败的线程进入等待队列等待唤醒。AQS 提供了锁的统一实现机制,支持公平锁、非公平锁等功能。
🎯 面试追问
Q1:AQS 核心是什么?
答:
state 状态变量 + FIFO 等待队列。
Q2:ReentrantLock 为什么需要 finally unlock?
答:
因为它是 Java 类,不是 JVM 关键字,需要手动释放锁。
Q3:FIFO 是什么?
答:
First In First Out,先进先出。
Q4:AQS 为什么需要队列?
答:
避免线程一直 CAS 自旋消耗 CPU。
Q5:ReentrantLock 默认公平吗?
答:
不是。
默认是非公平锁。
Q6:公平锁和非公平锁区别?
答:
公平锁按照等待顺序获取锁。
非公平锁允许新线程抢锁。
Q7:AQS 和 CAS 有什么关系?
答:
AQS 使用 CAS 修改 state,实现线程竞争锁。
⚠️ 工作踩坑
1. ReentrantLock 忘记 unlock
问题:
锁无法释放。
2. 认为 AQS 是一把锁
错误:
AQS 是锁框架。
ReentrantLock 才是具体锁。
3. 认为 FIFO 就一定公平
错误:
AQS 默认非公平,新线程可能抢锁。
❓ 自测
AQS 是什么?
AQS 核心组成是什么?
ReentrantLock 为什么基于 AQS?
state 表示什么?
FIFO 是什么意思?
为什么需要等待队列?
公平锁和非公平锁区别?
synchronized 和 ReentrantLock 区别?
为什么 ReentrantLock 需要 finally unlock?