Appearance
AQS 独占模式源码解析
一、AQS基础概念
- AbstractQueuedSynchronizer(抽象队列同步器)
- CLH队列变种实现
- volatile int state 核心状态
- 独占模式与共享模式区别
二、Node节点结构
java
static final class Node {
// 节点状态字段
volatile int waitStatus;
// 队列指针
volatile Node prev, next;
// 线程引用
volatile Thread thread;
}
三、acquire方法全流程解析(深度注解版)
java
/**
* 独占模式获取同步状态的完整流程
* 包含尝试获取、入队等待、线程阻塞等复合操作
*
* @param arg 请求的同步状态量(如锁的计数)
*/
public final void acquire(int arg) {
// 核心执行逻辑包含两个关键阶段:
if (!tryAcquire(arg) && // 快速尝试获取失败(非阻塞)
// 队列化获取过程(阻塞语义):
acquireQueued(
addWaiter(Node.EXCLUSIVE), // 将当前线程包装为独占节点加入等待队列
arg)) // 实际请求的同步状态量
// 特殊处理:若在等待过程中发生中断,进行最终中断标记设置
selfInterrupt(); // Thread.currentThread().interrupt()
}
四、关键方法深度剖析
1. tryAcquire方法(以ReentrantLock为例)
java
/**
* 独占锁获取尝试实现(ReentrantLock核心逻辑)
*
* 执行流程包含两个主要分支:
* - 首次获取锁:当state=0时,通过CAS+公平性检查获取锁
* - 可重入获取:已持有锁的线程再次获取时更新重入计数
*
* @param acquires 请求获取的同步状态量(通常为1)
* @return 是否成功获取锁
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState(); // 获取当前同步状态
// 首次获取锁场景
if (c == 0) {
// 公平性保障机制:
// 检查CLH队列中是否存在等待节点(避免插队)
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) { // 原子化状态更新
// 成功获取锁后设置独占线程
setExclusiveOwnerThread(current);
// 调试信息:可添加日志记录
// System.out.println(current.getName() + " acquired lock");
return true;
}
}
// 可重入获取场景
else if (current == getExclusiveOwnerThread()) {
// 计算新的重入次数
int nextc = c + acquires;
// 安全边界检查:防止int溢出导致的状态异常
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 更新同步状态(非原子操作,因为此时线程已持有锁)
setState(nextc);
// 调试信息:可添加日志记录
// System.out.println(current.getName() + " re-acquired lock, count: " + nextc);
return true;
}
// 锁已被其他线程占用或竞争失败
return false;
}
2. acquireQueued方法核心循环(深度注解版)
java
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true; // 标记是否发生异常中断
try {
boolean interrupted = false; // 记录线程是否被中断
// 自旋等待获取同步状态的核心循环
for (;;) {
// 获取当前节点的前驱节点
final Node p = node.predecessor();
// 当且仅当:
// 1. 当前节点是head的直接后继节点
// 2. 并且成功获取到同步状态(tryAcquire成功)
if (p == head && tryAcquire(arg)) {
// 成功获取锁后的清理操作:
setHead(node); // 将当前节点设置为头节点
p.next = null; // 原头节点置空,帮助GC回收
failed = false; // 标记获取成功
return interrupted; // 返回中断标志(可能被其他线程中断过)
}
// 判断是否需要进入阻塞状态:
// shouldParkAfterFailedAcquire:检查并更新节点状态
// parkAndCheckInterrupt:若需阻塞则执行park,并返回中断状态
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true; // 更新中断标记
}
} finally {
// 异常退出时的清理逻辑:
// 比如线程被中断、抛出异常等情况
if (failed)
cancelAcquire(node); // 取消该节点的获取请求
}
}
五、线程阻塞与唤醒机制
1. 底层实现原理
AQS通过 LockSupport.park() 和 unpark() 实现线程调度,相较于传统 synchronized 的 wait/notify 机制,具有以下优势:
- 无需持有对象监视器即可阻塞线程
- 精确控制线程唤醒(unpark(Thread))
- 基于 UNSAFE 类实现,避免竞态条件问题
2. waitStatus状态管理
同步队列中的每个 Node 节点维护线程状态字段 waitStatus
,定义如下:
- CANCELLED (1):节点线程被取消,进入终态
- SIGNAL (-1):后续节点需被唤醒
- CONDITION (-2):节点等待在条件变量上
- PROPAGATE (-3):共享模式下传播唤醒状态
典型状态转换场景:
触发动作 | 状态变化 | 说明 |
---|---|---|
获取锁失败 | 初始化 → SIGNAL | 标记后续线程可被唤醒 |
当前线程被中断 | SIGNAL → CANCELLED | 清理无效节点 |
唤醒后检查状态 | SIGNAL → 0 | 成功获取锁后重置状态 |
共享模式传播唤醒 | PROPAGATE → 0 | 确保唤醒操作向后传递 |
3. 中断响应策略
AQS提供两种中断处理方式:
- acquire(int arg)
忽略中断请求,仅在获取锁后更新中断标志位 - acquireInterruptibly(int arg)
直接抛出 InterruptedException,适用于需及时响应中断的场景
六、release方法解析
java
public final boolean release(int arg) {
// 1. 尝试释放同步状态(由子类实现)
if (tryRelease(arg)) {
Node h = head;
// 2. 如果头节点存在且等待状态非0(说明可能有后续等待线程需要唤醒)
if (h != null && h.waitStatus != 0)
// 3. 唤醒后继节点中的线程
unparkSuccessor(h);
return true;
}
return false;
}
1. tryRelease实现(ReentrantLock)深度解析
java
/**
* 独占锁释放核心方法(可重入语义支持)
*
* @param releases 请求释放的同步状态量
* @return 是否完全释放了占用权(state == 0)
*/
protected final boolean tryRelease(int releases) {
// 计算新的同步状态值
int c = getState() - releases;
// 安全检查:确保当前线程是锁持有者
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException("非持有锁线程尝试释放");
boolean free = false;
// 可重入退出逻辑:当state递减到0时才真正释放锁
if (c == 0) {
free = true; // 标记锁已释放
setExclusiveOwnerThread(null); // 清除线程占有标识
}
// 更新同步状态(可能为重入释放场景)
setState(c);
// 返回释放结果:
// - 可重入场景:返回false,锁仍被持有
// - 完全释放:返回true,资源可供其他线程竞争
return free;
}
七、设计模式与性能优化
- 模板方法模式应用
- CAS+自旋+阻塞的优化策略
- 队列维护的原子性保证
- 伪共享避免技巧