AQS

xiaopeng
发布于 2026-08-13 / 0 阅读
0
0

AQS

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 区别

synchronized

ReentrantLock

实现

JVM关键字

Java类

释放锁

自动

手动

公平锁

不支持

支持

尝试获取

不支持

支持

超时获取

不支持

支持

底层

Monitor

AQS


十一、为什么 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 默认非公平,新线程可能抢锁。


❓ 自测

  1. AQS 是什么?

  2. AQS 核心组成是什么?

  3. ReentrantLock 为什么基于 AQS?

  4. state 表示什么?

  5. FIFO 是什么意思?

  6. 为什么需要等待队列?

  7. 公平锁和非公平锁区别?

  8. synchronized 和 ReentrantLock 区别?

  9. 为什么 ReentrantLock 需要 finally unlock?


评论