AbstractQueuedSynchronizer (AQS) 是Java并发包(java.util.concurrent)的核心基础设施,它为锁(Lock)、信号量(Semaphore)、栅栏(CyclicBarrier)、闭锁(CountDownLatch)等多种同步器提供了一个统一的框架。通过解析AQS的源码并深入分析其典型实现,我们可以更好地理解Java并发机制的工作原理。
1. AQS 基础架构与源码分析
1.1 AQS 的核心成员
AQS 的核心包括以下几个关键成员变量和方法:
// 用于表示同步状态的变量
private volatile int state;
// 同步队列的头节点
private transient volatile Node head;
// 同步队列的尾节点
private transient volatile Node tail;
// 尝试获取独占锁,由子类实现
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// 尝试释放独占锁,由子类实现
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
// 尝试获取共享锁,由子类实现
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
// 尝试释放共享锁,由子类实现
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
state 是AQS的核心变量,它用于表示同步状态。head 和 tail 是一个双向链表结构,用于管理等待的线程。
1.2 线程排队机制
AQS 使用一个双向链表作为同步队列,这个链表的节点是 Node,每个节点对应一个线程。节点中的关键字段包括:
static final class Node {
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
}
waitStatus 用于表示线程的等待状态,prev 和 next 指向前后节点,形成一个双向链表结构。thread 存放当前节点对应的线程引用。
1.3 获取与释放同步状态
AQS 提供了 acquire 和 release 方法来进行同步状态的获取与释放:
-
acquire 方法的核心逻辑是尝试获取锁(通过调用
tryAcquire),如果获取失败,则将当前线程加入同步队列中,直到获取成功。public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } -
release 方法则是释放同步状态(调用
tryRelease),并唤醒同步队列中的下一个节点。public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
2. ReentrantLock 的源码与实现
ReentrantLock 是AQS的一个典型实现,它通过AQS的独占模式实现了可重入锁。ReentrantLock 分为公平锁和非公平锁,分别对应 FairSync 和 NonfairSync 内部类。
2.1 非公平锁的实现
NonfairSync 是 ReentrantLock 的默认实现,它允许“插队”,提高了性能:
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(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)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
lock() 方法首先尝试使用 CAS 直接获取锁,如果失败则通过 acquire(1) 进入 AQS 的获取逻辑。tryAcquire 方法用于尝试获取锁,非公平锁的获取并不考虑队列中的顺序,而是直接尝试抢占。
2.2 公平锁的实现
FairSync 则严格按照FIFO顺序分配锁,避免“插队”:
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
}
FairSync 的 tryAcquire 方法在获取锁前,会先检查队列中是否有等待的线程(hasQueuedPredecessors),确保锁的公平性。
3. Semaphore 的源码与实现
Semaphore 是基于AQS共享模式实现的信号量,允许多个线程同时访问资源。Semaphore 可以控制同时访问的线程数。
3.1 信号量的实现
Semaphore 的主要方法是 acquire 和 release:
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void release() {
sync.releaseShared(1);
}
acquire 方法通过 acquireSharedInterruptibly 获取共享资源,而 release 方法通过 releaseShared 释放资源。
3.2 共享模式的实现
tryAcquireShared 和 tryReleaseShared 是 Semaphore 的核心逻辑:
protected int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining))
return remaining;
}
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (compareAndSetState(current, next))
return true;
}
}
tryAcquireShared 方法在循环中通过CAS尝试减少信号量,当信号量足够时返回剩余的资源数。tryReleaseShared 方法则是增加信号量。
4. CountDownLatch 的源码与实现
CountDownLatch 是一个同步工具类,用于协调一组线程的执行,直到计数器减到零。
4.1 闭锁的实现
CountDownLatch 主要依赖于 await 和 countDown 方法:
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void countDown() {
sync.releaseShared(1);
}
await 方法会等待计数器减到零,而 countDown 方法则是减少计数器。
4.2 共享模式的实现
CountDownLatch 的 Sync 类实现了 tryAcquireShared 和 tryReleaseShared 方法:
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
tryAcquireShared 在 await 时检查计数器是否为零,如果为零则可以通过。tryReleaseShared 则减少计数器,当减到零时唤醒等待的线程。
5. CyclicBarrier 的源码与实现
CyclicBarrier 是一种同步辅助类,它允许一组线程互相等待,直到到达一个共同的屏障点。不同于 CountDownLatch,CyclicBarrier 可以重用。
5.1 栅栏的实现
CyclicBarrier 的核心方法是 await:
public int await() throws InterruptedException, BrokenBarrierException {
return dowait(false, 0L);
}
5.2 屏障的管理
CyclicBarrier 使用了一个计数器和一个屏障点来管理线程的等待和唤醒:
private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
int index = --count;
if (index == 0) { // tripped
nextGeneration();
return 0;
}
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
breakBarrier();
throw ie;
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
}
} finally {
lock.unlock();
}
}
当最后一个线程到达时,屏障会重置,并唤醒所有等待的线程。否则,线程进入等待状态,直到屏障被触发。
6. 总结
通过深入剖析AQS的源码及其典型实现如 ReentrantLock、Semaphore、CountDownLatch 和 CyclicBarrier,我们可以看到AQS如何通过一种通用的机制来实现各种复杂的同步需求。AQS 的设计极其灵活和高效,但其复杂性也要求开发者具备较深的并发编程知识。通过掌握AQS的工作原理,我们可以更好地应用和扩展Java的并发工具,更有效地解决实际中的并发问题。