AQS(AbstractQueuedSynchronizer) 是 Java 并发编程中的一个核心框架,位于 java.util.concurrent.locks
包中。它是构建锁和其他同步工具(如 ReentrantLock
、CountDownLatch
、Semaphore
等)的基础。AQS 通过一个 FIFO 队列(双向链表)来管理线程的排队和唤醒,并提供了一种灵活的机制来实现自定义的同步器。
1. AQS 的核心思想
- 资源共享模式:
- AQS 支持两种资源共享模式:
- 独占模式(Exclusive):同一时刻只有一个线程可以访问资源,如
ReentrantLock
。 - 共享模式(Shared):同一时刻可以有多个线程访问资源,如
Semaphore
、CountDownLatch
。
- 状态管理:
- AQS 通过一个
volatile int state
变量来表示资源的状态。 - 子类可以通过
getState()
、setState()
和compareAndSetState()
方法来操作状态。
- AQS 通过一个
- 线程排队:
- AQS 使用一个双向链表(CLH 队列)来管理等待获取资源的线程。
- 每个线程被封装为一个
Node
对象,包含线程引用、等待状态等信息。
2. AQS 的核心方法
- 独占模式:
tryAcquire(int arg)
:尝试获取资源,需要子类实现。acquire(int arg)
:调用tryAcquire
,如果失败则将线程加入队列并阻塞。tryRelease(int arg)
:尝试释放资源,需要子类实现。release(int arg)
:调用tryRelease
,并唤醒队列中的下一个线程。
- 共享模式:
tryAcquireShared(int arg)
:尝试获取共享资源,需要子类实现。acquireShared(int arg)
:调用tryAcquireShared
,如果失败则将线程加入队列并阻塞。tryReleaseShared(int arg)
:尝试释放共享资源,需要子类实现。releaseShared(int arg)
:调用tryReleaseShared
,并唤醒队列中的线程。
3. AQS 的实现原理
- 状态变量(state):
state
是 AQS 的核心变量,表示资源的状态。- 例如,在
ReentrantLock
中,state
表示锁的重入次数;在Semaphore
中,state
表示剩余的许可数。
- CLH 队列:
- AQS 使用一个双向链表(CLH 队列)来管理等待线程。
- 每个
Node
包含线程引用、等待状态(如CANCELLED
、SIGNAL
)等信息。
- 线程阻塞与唤醒:
- 当线程获取资源失败时,会被封装为
Node
并加入队列,然后通过LockSupport.park()
阻塞。 - 当资源释放时,AQS 会唤醒队列中的下一个线程(通过
LockSupport.unpark()
)。
- 当线程获取资源失败时,会被封装为
4. AQS 的应用
- ReentrantLock:
ReentrantLock
是基于 AQS 实现的独占锁,支持可重入和公平/非公平模式。
- Semaphore:
Semaphore
是基于 AQS 实现的共享锁,用于控制同时访问资源的线程数。
- CountDownLatch:
CountDownLatch
是基于 AQS 实现的同步工具,用于等待一组线程完成任务。
- ReentrantReadWriteLock:
ReentrantReadWriteLock
是基于 AQS 实现的读写锁,支持读多写少的场景。
5. AQS 的代码示例
示例 1:自定义独占锁
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) {
if (compareAndSetState(0, 1)) { // CAS 操作
setExclusiveOwnerThread(Thread.currentThread()); // 设置独占线程
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null); // 清除独占线程
setState(0); // 释放锁
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
public void lock() {
sync.acquire(1);
}
public void unlock() {
sync.release(1);
}
}
示例 2:使用自定义锁
public class MyLockExample {
private static final MyLock lock = new MyLock();
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Final count: " + count); // 输出 2000
}
}
6. AQS 的优缺点
- 优点:
- 灵活性:AQS 提供了基础的同步框架,可以轻松实现各种同步工具。
- 高性能:通过 CAS 和 CLH 队列,AQS 在高并发场景下性能优异。
- 缺点:
- 复杂性:AQS 的实现较为复杂,需要深入理解其原理。
- 扩展性:自定义同步器需要实现 AQS 的模板方法,对开发者要求较高。
7. 总结
- AQS 是 Java 并发编程的核心框架,用于构建锁和其他同步工具。
- 它通过状态变量和 CLH 队列管理线程的排队和唤醒,支持独占模式和共享模式。
ReentrantLock
、Semaphore
、CountDownLatch
等工具都是基于 AQS 实现的。- 理解 AQS 的原理对于掌握 Java 并发编程至关重要。
THE END
暂无评论内容