AbstractQueuedSynchronizer(AQS)JDK1.8 源码分析
一、核心概念
1.1 核心实现机制
1.1.1 可重入性
AQS的重入性(Reentrant)是指同一个线程能够多次获取同一个锁而不会死锁。
- 什么是重入性问题:
public class Example { private final ReentrantLock lock = new ReentrantLock(); public void outer() { lock.lock(); // 第一次获取锁 try { inner(); // 调用inner方法 } finally { lock.unlock(); } } public void inner() { lock.lock(); // 同一线程第二次获取锁 try { // do something } finally { lock.unlock(); } } } - 为什么需要重入性:
- 避免死锁:如果不支持重入,上述代码会死锁
- 方法嵌套调用:同一个线程内的方法调用链需要多次获取相同的锁
- 继承场景:子类方法调用父类的同步方法时也需要重入
- AQS如何实现重入:
// ReentrantLock.Sync类中非公平锁的实现 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 锁未被持有 if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { // 检查是否是当前线程持有锁 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); // 增加重入计数 return true; } return false; } - 使用注意事项:
- 注意unlock的次数要与lock的次数匹配
- 合理使用try-finally确保锁的释放
- 避免过深的重入,可能影响性能
- 考虑使用Lock接口的其他方法(如tryLock)来增加灵活性
1.1.2 公平性&非公平性
AQS的公平锁和非公平锁,在实现方式上的区别是,在锁空闲时,是否调用了hasQueuedPredecessors()方法,该方法用于检查队列是否有前驱节点。
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 关键区别:公平锁会先调用hasQueuedPredecessors()方法检查队列是否有前驱节点 if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
1.1.3 中断处理
1.2 核心属性
public abstract class AbstractQueuedSynchronizer {
/** 同步状态 */
private volatile int state;
/** 等待队列的头节点 */
private transient volatile Node head;
/** 等待队列的尾节点 */
private transient volatile Node tail;
}
1.3 Node详解
static final class Node {
/** 表示共享模式 */
static final Node SHARED = new Node();
/** 表示独占模式 */
static final Node EXCLUSIVE = null;
/**
* waitStatus 值,表示线程已取消(值为1)
* 由于超时或中断,节点会被设置为取消状态,被取消的节点时不会再变化状态
*/
static final int CANCELLED = 1;
/**
* waitStatus 值,表示后继线程需要被唤醒(值为-1)
* 后继节点入队时,会将前驱节点设置为 SIGNAL
*/
static final int SIGNAL = -1;
/**
* waitStatus 值,表示节点在条件队列中(值为-2)
* 当前节点在 condition 队列中等待
*/
static final int CONDITION = -2;
/**
* waitStatus 值,用于共享模式,表示状态需要向后传播(值为-3)
* 在共享模式下,PROPAGATE 状态的节点会向后传播唤醒的操作
*/
static final int PROPAGATE = -3;
/**
* 等待状态,初始值为 0
* 可能值包括:CANCELLED(1)、SIGNAL(-1)、CONDITION(-2)、PROPAGATE(-3)、0(初始状态)
*/
volatile int waitStatus;
/** 前驱节点的引用 */
volatile Node prev;
/** 后继节点的引用 */
volatile Node next;
/** 当前节点封装的线程 */
volatile Thread thread;
/** 下一个等待节点,用于条件队列 */
Node nextWaiter;
}
二、加锁实现详解

2.1 acquire() 方法 - 获取锁的入口
public final void acquire(int arg) {
// 1. 尝试获取锁,如果成功则直接返回
// 2. 获取失败,将当前线程包装成 Node 并加入队列
// 3. acquireQueued 返回 true 表示在等待过程中被中断
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt(); // 重新设置中断状态
}
2.2 tryAcquire() - 尝试获取锁
这里以 ReentrantLock 的公平锁为例:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { // 锁未被占用
// 检查前面是否已经有在排队的,为实现公平性
if (!hasQueuedPredecessors() &&
// CAS不成功则说明同时有其他线程竞争到了锁
compareAndSetState(0, acquires)) {
// 能进来说明已经获取到锁,这里标记一下(独占线程)
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 处理重入的情况
int nextc = c + acquires;
if (nextc < 0) // 检查溢出
throw new Error("Maximum lock count exceeded");
setState(nextc); // 设置重入次数
return true;
}
return false;
}
2.3 addWaiter() - 加入等待队列
private Node addWaiter(Node mode) {
// 创建新节点,将当前线程塞进去
Node node = new Node(Thread.currentThread(), mode);
// 判断队列是否已经初始化,如果已经初始化,则尝试快速入队(尾部入队)
Node pred = tail;
if (pred != null) {
// 如果队列已初始化,则首先将当前队尾,设置为当前node的前驱
node.prev = pred;
// 然后,使用 CAS 把当前node设置成队尾
if (compareAndSetTail(pred, node)) {
// CAS成功,说明当前node已经成为队尾。
// 紧接着将自己与之前的队尾相连,结合前面的{node.prev = pred} 这样就实现了和之前的尾结点双向连接了。
pred.next = node;
return node;
}
// 如果上面的CAS失败,说明有线程在竞争入队
}
// 如果队列未初始化 或者 快速入队失败(CAS失败,有其他线程竞争入队),则进入完整入队方法(自旋方式入队)
enq(node);
return node;
}
// 自旋方式入队
private Node enq(final Node node) {
for (;;) {
Node t = tail;
// 判断是否需要初始化队列
if (t == null) {
// 进行初始化
// 这里为什么要CAS呢,因为可能同时有多个线程进来
if (compareAndSetHead(new Node()))
// 注意:这里并没有return!!! 所以紧接着下一次循环就会进到下面的else
tail = head;
} else {
// 这里的逻辑和addWaiter()中的快速入队一样,都是为了把当前节点设置成队尾,设置成功后结束自旋。
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
2.4 acquireQueued() - 自旋获取锁
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; // 帮助 GC
failed = false;
return interrupted;
}
// 判断获取锁失败后,是否需要阻塞(挂起当前线程)
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
// tryAcquire()抛异常的情况下,会进到这里
cancelAcquire(node);
}
}
2.5 shouldParkAfterFailedAcquire() - 检查是否需要阻塞
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) // 前驱节点已经准备好唤醒后继节点
return true;
if (ws > 0) { // 前驱节点已取消,跳过取消的节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else { // 将前驱节点设置为SIGNAL状态
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
三、解锁实现详解
3.1 release() - 释放锁
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒后继节点
return true;
}
return false;
}
3.2 unparkSuccessor() - 唤醒后继节点
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
// 如果后继节点为空或已取消,从尾部开始找到最前面的一个未取消的节点
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); // 唤醒线程
}
四、Node 状态转换
4.1 状态值定义
- CANCELLED (1):线程已取消
- SIGNAL (-1):后继节点需要唤醒
- CONDITION (-2):节点在条件队列中
- PROPAGATE (-3):状态需要向后传播
- 0:初始状态
4.2 状态转换流程图
初始状态(0)
↙ ↓ ↘
SIGNAL CANCELLED CONDITION
↓ ↓ ↓
PROPAGATE (终态) SIGNAL
4.3 主要状态转换场景
-
初始状态 → SIGNAL:
场景:后继节点入队时
目的:标识后继节点需要被唤醒 -
初始状态 → CANCELLED:
场景:线程等待超时或被中断
目的:标识节点已失效 -
CONDITION → SIGNAL:
场景:条件满足,节点从条件队列转移到同步队列
目的:重新参与锁的竞争
六、性能优化
6.1 CAS操作
- 使用 CAS 避免锁竞争
- 快速设置 tail 节点
- 原子更新状态
// Code Example:
public class CASExample extends AbstractQueuedSynchronizer {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset(
AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset(
AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset(
AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
} catch (Exception ex) { throw new Error(ex); }
}
// CAS更新状态
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
}
6.2 自旋优化
- 尝试获取锁时先自旋
- 减少不必要的线程阻塞
- 适应性自旋
// Code Example:
public class SpinningExample extends AbstractQueuedSynchronizer {
private static final int MAX_SPIN_COUNT = 100;
public void lock() {
// 先尝试自旋获取
int spinCount = 0;
while (spinCount < MAX_SPIN_COUNT) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return;
}
spinCount++;
// 适应性自旋:如果上次自旋成功,则多自旋几次
if (spinCount < previousSuccessfulSpins * 2) {
Thread.onSpinWait(); // Java 9+ specific
}
}
// 自旋失败,进入正常的锁获取流程
acquire(1);
}
}
6.3 队列优化
- 快速入队尝试
- 双向链表结构
- 跳过取消节点
// Code Example:
public class QueueOptimization extends AbstractQueuedSynchronizer {
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
// 初始化队列
if (compareAndSetHead(new Node())) {
tail = head;
}
} else {
// 快速入队尝试
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
// 处理取消的节点
Node pred = node.prev;
while (pred.waitStatus > 0) {
node.prev = pred = pred.prev;
}
pred.next = node;
}
}
}
}
JDK1.8 源码分析&spm=1001.2101.3001.5002&articleId=143432294&d=1&t=3&u=d2f82879f13041499d8cf3e290a3abad)
1261

被折叠的 条评论
为什么被折叠?



