目录
引言
在 Java 并发编程中,AbstractQueuedSynchronizer
(简称 AQS)是一个非常重要的框架。它是许多并发工具类(如 ReentrantLock
、Semaphore
、CountDownLatch
等)的基础。AQS 提供了一种实现阻塞锁和同步器的框架,开发者可以基于 AQS 轻松构建自定义的同步器。
本文将深入探讨 AQS 的核心思想、工作原理、源码实现以及如何使用 AQS 构建自定义同步器。
1. 什么是 AQS?
AbstractQueuedSynchronizer
是 Java 并发包(java.util.concurrent
)中的一个抽象类,用于构建锁和其他同步器。它的核心思想是:
-
同步状态管理:AQS 通过一个
volatile
的int
类型变量(state
)来表示同步状态。 -
线程阻塞与唤醒:AQS 使用一个 FIFO 队列(双向链表)来管理等待获取锁的线程。
-
模板方法模式:AQS 提供了一些模板方法(如
tryAcquire
、tryRelease
等),子类可以通过重写这些方法来实现自定义的同步逻辑。
2. AQS 的核心组件
2.1 同步状态(state
)
state
是 AQS 的核心变量,用于表示同步器的状态。它的具体含义由子类定义。例如:
-
在
ReentrantLock
中,state
表示锁的重入次数。 -
在
Semaphore
中,state
表示剩余的许可数量。 -
在
CountDownLatch
中,state
表示剩余的计数。
state
是一个 volatile
变量,保证了多线程环境下的可见性。
2.2 等待队列
AQS 使用一个 FIFO 队列(双向链表)来管理等待获取锁的线程。队列中的每个节点(Node
)代表一个等待线程。节点有两种模式:
-
独占模式(Exclusive):同一时刻只有一个线程可以获取锁(如
ReentrantLock
)。 -
共享模式(Shared):同一时刻可以有多个线程获取锁(如
Semaphore
)。
3. AQS 的工作原理
AQS 的核心思想是通过 state
和等待队列来实现线程的阻塞与唤醒。以下是 AQS 的主要工作流程:
3.1 获取锁(acquire
)
-
线程调用
acquire(int arg)
方法尝试获取锁。 -
调用子类实现的
tryAcquire(int arg)
方法尝试获取锁。-
如果成功,则线程获取锁,继续执行。
-
如果失败,则将线程包装成
Node
节点,加入等待队列。
-
-
线程在队列中自旋或阻塞,直到被前驱节点唤醒。
3.2 释放锁(release
)
-
线程调用
release(int arg)
方法释放锁。 -
调用子类实现的
tryRelease(int arg)
方法释放锁。 -
唤醒等待队列中的下一个线程。
4. AQS 的源码实现
4.1 同步状态(state
)
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
4.2 等待队列
AQS 的等待队列是一个双向链表,节点定义如下:
static final class Node {
volatile int waitStatus; // 等待状态
volatile Node prev; // 前驱节点
volatile Node next; // 后继节点
volatile Thread thread; // 等待线程
Node nextWaiter; // 共享模式或独占模式
}
4.3 获取锁(acquire
)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
-
tryAcquire(int arg)
:子类实现,尝试获取锁。 -
addWaiter(Node mode)
:将当前线程包装成Node
节点,加入等待队列。 -
acquireQueued(Node node, int arg)
:线程在队列中自旋或阻塞,直到获取锁。
4.4 释放锁(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;
}
-
tryRelease(int arg)
:子类实现,尝试释放锁。 -
unparkSuccessor(Node node)
:唤醒等待队列中的下一个线程。
5. 如何使用 AQS 实现自定义同步器
以下是一个基于 AQS 实现的简单互斥锁:
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class MyMutex {
private static class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) { // 尝试将 state 从 0 改为 1
setExclusiveOwnerThread(Thread.currentThread()); // 设置当前线程为独占线程
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null); // 清空独占线程
setState(0); // 将 state 重置为 0
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
private final Sync sync = new Sync();
public void lock() {
sync.acquire(1);
}
public void unlock() {
sync.release(1);
}
}
6. AQS 的应用场景
AQS 是许多并发工具类的基础,例如:
-
ReentrantLock:可重入锁。
-
Semaphore:信号量。
-
CountDownLatch:倒计时门闩。
-
ReentrantReadWriteLock:可重入读写锁。
7. 总结
AQS 是 Java 并发编程的核心框架,通过 state
和等待队列实现了高效的线程同步机制。理解 AQS 的工作原理和源码实现,有助于深入掌握 Java 并发编程,并能够基于 AQS 构建自定义的同步器。