谈谈你对 AQS(AbstractQueuedSynchronizer)的理解?
引言:
"线程安全"是每一个 Java 程序员绕不开的关键词。你是否也曾在代码中使用过
ReentrantLock
、CountDownLatch
、Semaphore
,却对它们内部的实现一知半解?作为一名有 8年开发经验的 Java 工程师 ,我想和你聊聊这些并发工具背后的核心 ------ AbstractQueuedSynchronizer(简称 AQS) 。这是一个你了解之后就会忍不住想说一句:"这设计,真香!"
一、AQS 是什么?为什么要了解它?
在日常的业务开发中,我们经常会遇到如下场景:
- 限流:控制并发访问某个资源的线程数量(如:短信接口、支付接口)
- 任务协调:多个线程等待一个共同条件完成后再执行(如:并发初始化)
- 独占资源:某一时刻只能有一个线程访问某段代码(如:库存扣减)
这些场景中,Semaphore
、CountDownLatch
、ReentrantLock
等工具类频繁登场。而它们的底层实现几乎都基于同一个类:AbstractQueuedSynchronizer
。
AQS 是 J.U.C(java.util.concurrent)中一个非常核心的同步框架,提供了一种基于 FIFO 队列 的线程同步机制。它本身并不直接实现同步逻辑,而是通过子类实现具体的同步语义。
二、业务场景:模拟一个限流器
假设我们在做一个电商系统,需要限制同时进行支付操作的用户数为 3。超过的用户需要等待。
这时候我们可以使用 Semaphore
,而 Semaphore
的核心正是基于 AQS 实现的。
三、从业务到源码:一步步剖析 AQS
3.1 使用场景代码示例
java
import java.util.concurrent.Semaphore;
public class PaymentService {
// 允许最多3个线程同时执行支付
private final Semaphore semaphore = new Semaphore(3);
public void pay(String user) {
try {
semaphore.acquire(); // 获取许可,占用一个名额
System.out.println(user + " 正在支付...");
Thread.sleep(2000); // 模拟支付耗时
System.out.println(user + " 支付完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release(); // 释放许可
}
}
public static void main(String[] args) {
PaymentService service = new PaymentService();
for (int i = 1; i <= 10; i++) {
final String user = "用户" + i;
new Thread(() -> service.pay(user)).start();
}
}
}
3.2 Semaphore 源码架构简析
java
public class Semaphore implements java.io.Serializable {
// 内部使用Sync表示同步器
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) {
setState(permits); // 初始许可数
}
final int getPermits() {
return getState();
}
// 尝试获取许可
protected final boolean tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining)) {
return remaining >= 0;
}
}
}
// 释放许可
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (compareAndSetState(current, next))
return true;
}
}
}
// 非公平实现
static final class NonfairSync extends Sync {
NonfairSync(int permits) {
super(permits);
}
}
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void release() {
sync.releaseShared(1);
}
}
3.3 AQS 核心机制
- state:一个 int 类型的共享变量,用来表示同步状态(如剩余许可数)
- 队列:当线程获取失败时,会被加入一个 CLH 队列进行排队
- 模板方法模式:AQS 提供了 acquire、release 等公共方法,底层调用子类实现的 tryAcquire、tryRelease 等方法
四、自己实现一个简单的锁(基于 AQS)
我们可以基于 AQS 自定义一个不可重入的独占锁,来更深入理解它的使用。
java
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class MyLock {
private final Sync sync = new Sync();
private static class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
// state == 0 表示当前未被占用
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0 || getExclusiveOwnerThread() != Thread.currentThread()) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1 && getExclusiveOwnerThread() == Thread.currentThread();
}
}
public void lock() {
sync.acquire(1);
}
public void unlock() {
sync.release(1);
}
}
使用自定义锁
csharp
public class TestMyLock {
private static final MyLock lock = new MyLock();
public static void main(String[] args) {
Runnable task = () -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取了锁");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放了锁");
}
};
for (int i = 0; i < 5; i++) {
new Thread(task).start();
}
}
}
五、总结
- AQS 是 Java 并发包中的基石,几乎所有同步工具都基于它。
- 它的核心思想是使用一个共享变量(state)结合队列等待机制,让线程在高并发下能有序、公平地竞争资源。
- 理解 AQS,不仅能更好地使用已有的并发工具,也能根据业务场景实现自己的同步器。
六、结语
作为一名有多年开发经验的程序员,理解原理是一种责任,也是一种乐趣。AQS 虽然看起来复杂,但其实设计非常优雅。一旦你真正理解了这个框架,很多并发问题就能迎刃而解。
如果你觉得本文对你有帮助,欢迎点赞、收藏、转发。