AbstractQueuedSynchronizer(AQS)是Java并发编程的核心框架,为构建锁和同步器提供了通用基础设施。本文将全面解析AQS的核心原理、工作机制,并通过实战案例展示其应用场景与最佳实践。
一、AQS核心原理与架构设计
1. AQS基本概念
AQS是Java并发包(java.util.concurrent.locks)中的抽象类,通过FIFO队列和状态变量实现线程同步机制。其核心设计思想包括:
- 模板方法模式:将同步器的核心逻辑封装在基类中,子类只需实现特定方法如tryAcquire、tryRelease等
- CLH队列变体:使用双向链表管理等待线程,支持公平锁和非公平锁实现
- 状态管理:通过volatile int类型的state变量表示资源状态,支持CAS原子操作
2. 核心组件
2.1 状态变量(state)
state是AQS的核心字段,不同同步器对其有不同语义:
- ReentrantLock:表示锁的重入次数
- Semaphore:表示可用许可证数量
- CountDownLatch:表示未完成的事件数
AQS提供三种原子操作方法管理state:
arduino
protected final int getState() { return state; }
protected final void setState(int newState) { state = newState; }
protected final boolean compareAndSetState(int expect, int update) {
// CAS操作保证原子性
}
2.2 同步队列(CLH队列)
AQS使用CLH变体队列管理等待线程,核心结构包括:
- 节点(Node):包含waitStatus(节点状态)、thread(关联线程)、prev/next(前后指针)
- 头节点(head):当前持有锁的线程节点
- 尾节点(tail):队列最后一个节点
节点状态包括:
- CANCELLED(1):节点因超时/中断被取消
- SIGNAL(-1):后继节点需要被唤醒
- CONDITION(-2):节点在条件队列中等待
- PROPAGATE(-3):共享模式下状态变更需要传播
3. 工作模式
3.1 独占模式(Exclusive Mode)
同一时间只有一个线程能获取资源,典型实现如ReentrantLock。核心流程:
获取锁(acquire)
scss
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- 调用tryAcquire尝试获取锁(子类实现)
- 失败则将线程包装为独占节点加入队列尾部
- 线程在队列中自旋检查,前驱为头节点时尝试获取锁
释放锁(release)
java
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释放锁(子类实现)
- 成功则唤醒后继节点
3.2 共享模式(Shared Mode)
多个线程可同时获取资源,典型实现如Semaphore、CountDownLatch。核心流程:
获取共享资源(acquireShared)
arduino
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
-
tryAcquireShared返回值:
- 负数:获取失败
- 0:获取成功但无剩余资源
- 正数:获取成功且有剩余资源
释放共享资源(releaseShared)
arduino
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
二、AQS在并发工具中的应用
1. ReentrantLock
基于AQS独占模式实现的可重入互斥锁:
-
state语义:锁的重入次数
-
公平性:
- 公平锁:按FIFO顺序获取锁
- 非公平锁:允许插队,性能更高
示例代码:
csharp
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// 临界区操作
} finally {
lock.unlock();
}
2. Semaphore
基于AQS共享模式实现的计数信号量:
- state语义:可用许可证数量
- 应用场景:资源池管理、限流控制
示例代码:
java
Semaphore semaphore = new Semaphore(10); // 最大10个许可
semaphore.acquire(); // 获取许可
try {
// 访问受限资源
} finally {
semaphore.release(); // 释放许可
}
3. CountDownLatch
基于AQS共享模式实现的一次性屏障:
- state语义:未完成的事件数
- 应用场景:多线程任务协调
示例代码:
arduino
CountDownLatch latch = new CountDownLatch(5);
// 工作线程
latch.countDown();
// 主线程
latch.await(); // 等待所有工作完成
4. ReentrantReadWriteLock
组合独占(写)和共享(读)模式的读写锁:
-
state语义:
- 高16位:读锁计数
- 低16位:写锁重入计数
-
应用场景:读多写少的并发访问
示例代码:
scss
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
rwLock.readLock().lock(); // 获取读锁
try {
// 读操作
} finally {
rwLock.readLock().unlock();
}
rwLock.writeLock().lock(); // 获取写锁
try {
// 写操作
} finally {
rwLock.writeLock().unlock();
}
三、AQS项目实战
1. 电商库存扣减(ReentrantLock)
高并发场景下保证库存扣减的原子性:
csharp
public class Inventory {
private int stock;
private final ReentrantLock lock = new ReentrantLock();
public boolean deduct() {
lock.lock();
try {
if (stock > 0) {
stock--;
return true;
}
return false;
} finally {
lock.unlock();
}
}
}
2. 自定义限流器(共享模式)
基于AQS实现简单的限流控制:
java
public class SimpleLimiter {
private static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) { setState(permits); }
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 (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
}
private final Sync sync;
public SimpleLimiter(int permits) { sync = new Sync(permits); }
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void release() {
sync.releaseShared(1);
}
}
四、AQS最佳实践与性能优化
1. 设计原则
- 正确实现模板方法:确保tryAcquire/tryRelease等方法的正确性,避免死锁
- 合理选择模式:根据场景选择独占或共享模式
- 状态设计:合理定义state变量的语义和取值范围
2. 性能优化
- 减少锁竞争:缩小临界区,缩短锁持有时间
- CAS优化:高并发场景下考虑LongAdder等替代方案
- 公平性权衡:非公平锁提高吞吐但可能导致饥饿
3. 常见问题与解决方案
- 死锁:确保锁的获取和释放成对出现,建议使用try-finally结构
- 饥饿:公平锁解决但性能较低,需权衡
- 性能瓶颈:监控锁竞争情况,优化同步策略
五、总结
AQS作为Java并发编程的基石,通过state状态管理和CLH队列机制,为构建各种同步组件提供了强大而灵活的基础设施。掌握AQS不仅有助于理解Java并发工具的实现原理,更能为构建高性能、高并发的自定义同步组件奠定基础。在实际开发中,应根据具体场景选择合适的同步模式和策略,平衡性能与公平性需求。