一、引子:修仙界的资源争夺
话说在Java修仙大陆,有一神秘宗门名为并发宗 。宗内有一至宝------灵脉核心,此物乃修炼圣地,得者可修为大进。
然灵脉唯一,弟子万千。如何公平分配?宗主Doug Lea 老祖遂创AQS大阵(AbstractQueuedSynchronizer),此乃镇宗之宝。
现实映射:
- 灵脉核心 = 临界区资源(如数据库连接、共享变量)
- 弟子 = 线程
- AQS大阵 =
ReentrantLock、Semaphore、CountDownLatch的底层实现
二、宗门大阵(AQS)的核心设计
2.1 阵眼:state变量
arduino
// AQS核心:一个volatile int,记录灵脉状态
private volatile int state;
| 修仙术语 | 技术含义 |
|---|---|
state = 0 |
灵脉空闲,无人占用 |
state = 1 |
被独占(单弟子修炼) |
state = N |
被共享(N个弟子同时修炼) |
volatile |
阵眼发光,全宗门可见(内存可见性) |
2.2 阵图:FIFO双端队列
scss
┌─────────────────────────────────────┐
│ AQS宗门大阵 │
│ │
│ head → [弟子A] ←→ [弟子B] ←→ [弟子C] ← tail
│ (正在修炼) (排队等待) (排队等待)
│ │
│ state = 1(灵脉被A独占) │
└─────────────────────────────────────┘
arduino
// AQS内部队列节点
static final class Node {
volatile int waitStatus; // 弟子状态:修炼中/等待/取消
volatile Node prev; // 前一位师兄
volatile Node next; // 后一位师弟
volatile Thread thread; // 弟子真身(线程)
}
三、独占锁:单传弟子的传承仪式
3.1 场景:ReentrantLock(可重入独占锁)
大长老宣布:灵脉核心一次只容一人修炼 ,但允许同一人多次进入(可重入)。
3.2 修仙流程图
ini
弟子A申请灵脉
│
▼
┌─────────────┐
│ tryAcquire() │ ← 阵眼state是否为0?
│ 尝试抢占 │
└─────────────┘
│
├── state=0 ──→ CAS修改state=1 ──→ 获得灵脉,开始修炼
│ │
│ ▼
│ 修炼完毕release()
│ state=0,唤醒队列下一个
│
└── state=1 ──→ 是同一人? ──→ state+1(重入次数+1)
│
└── 不同人? ──→ 加入FIFO队列, park()休眠等待
3.3 核心源码(修仙注释版)
ini
// 弟子尝试抢占灵脉(AQS.acquire简化版)
public final void acquire(int arg) {
// 第一步:tryAcquire,看运气能不能直接抢到
if (!tryAcquire(arg) &&
// 第二步:抢不到,加入宗门排队队列
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
// 第三步:排队期间被中断,补中断标记
selfInterrupt();
}
}
// 加入排队队列(像挂号)
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
Node pred = tail;
if (pred != null) { // 队列已有师兄
node.prev = pred;
if (compareAndSetTail(pred, node)) { // CAS挂到队尾
pred.next = node;
return node;
}
}
enq(node); // 队列空或CAS失败,自旋入队
return node;
}
// 排队等待(宗门规矩:按顺序来)
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) { // 自旋,心魔誓约:不放弃
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) { // 轮到我了!
setHead(node); // 成为阵首,开始修炼
p.next = null;
failed = false;
return interrupted;
}
// 还没轮到我,休眠等待被唤醒
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node); // 走火入魔,取消资格
}
}
四、共享锁:宗门宝库的开放日
4.1 场景:Semaphore(信号量)
宗主宣布:宝库中有10件灵器 ,可同时供10位弟子取用,先到先得。
4.2 核心区别
| 独占锁(ReentrantLock) | 共享锁(Semaphore) | |
|---|---|---|
| state含义 | 0或1(有人/无人) | N(剩余名额) |
| 获取成功条件 | state从0→1 | state从N→N-1(N>0) |
| 唤醒策略 | 只唤醒下一个 | 可能唤醒多个 |
4.3 源码差异
arduino
// 共享锁获取:只要还有名额,就允许进入
protected int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining; // >=0成功,<0失败
}
}
// 释放时:可能唤醒多个等待弟子
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared(); // 唤醒后继,传播唤醒
return true;
}
return false;
}
五、Condition:心魔誓约与传音入密
5.1 场景
弟子A修炼到关键瓶颈 ,需要特定条件(如"灵气浓度>80")才能突破。此时他:
- 暂时释放灵脉(让出位置)
- 进入静室等待(Condition队列)
- 条件满足后被唤醒(重新排队抢灵脉)
5.2 结构图
scss
┌─────────────────────────────────────────┐
│ AQS主队列 │
│ head → [弟子A] → [弟子B] → [弟子C] │
│ (修炼中) │
└─────────────────────────────────────────┘
│
▼ 调用 condition.await()
┌─────────────────────────────────────────┐
│ Condition静室队列 │
│ [弟子A] → [弟子D] (等待特定条件) │
│ (释放灵脉,在此休眠) │
└─────────────────────────────────────────┘
│
▼ 其他弟子调用 condition.signal()
唤醒弟子A,重新进入主队列排队
5.3 代码示例
csharp
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// 弟子A的修炼流程
lock.lock();
try {
while (灵气浓度 < 80) { // 防止虚假唤醒
condition.await(); // 释放锁,进静室等待
}
// 条件满足,继续修炼
突破瓶颈();
} finally {
lock.unlock();
}
// 弟子B(管理灵气):
lock.lock();
try {
增加灵气浓度();
condition.signalAll(); // 唤醒所有等待的弟子
} finally {
lock.unlock();
}
六、源码揭秘:从修仙术语到Java代码
| 修仙术语 | Java术语 | 源码位置 |
|---|---|---|
| 宗门大阵 | AQS | java.util.concurrent.locks.AbstractQueuedSynchronizer |
| 阵眼state | 同步状态 | private volatile int state |
| 弟子排队 | CLH队列 | Node head, tail |
| 单传弟子 | 独占模式 | Node.EXCLUSIVE |
| 多人同修 | 共享模式 | Node.SHARED |
| 心魔誓约 | Condition | ConditionObject |
| 传音入密 | LockSupport | LockSupport.park()/unpark() |
七、总结:AQS的修仙哲学
- state为阵眼:一切同步状态,皆系于此
- FIFO为天道:先来后到,公平有序
- CAS为法术:原子操作,无锁竞争
- LockSupport为休眠:不浪费灵气(CPU),等待唤醒
- 可重入为传承:同一线程多次进入,计数管理
AQS之所以强大 ,在于它将复杂的线程同步 抽象为简单的状态管理+队列调度,让万千弟子(线程)井然有序地争夺灵脉(资源)。