关于并发编程AQS的学习
目录
1. AQS的核心作用
2. AQS的核心结构
3. 关键方法
4. AQS的应用示例
4.1、ReentrantLock的实现
4.2、CountDownLatch的实现
5. AQS的优势
6. 对比其他同步机制
前言
AQS(AbstractQueuedSynchronizer) 是Java并发编程中一个核心的同步器框架,位于 java.util.concurrent.locks
包下,由Doug Lea设计。它是构建锁(如 ReentrantLock
)和其他同步工具(如 CountDownLatch
、Semaphore
)的基础抽象类。
如下图所示:
public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {private static final long serialVersionUID = 7373984972572414691L;/*** Creates a new {@code AbstractQueuedSynchronizer} instance* with initial synchronization state of zero.*/protected AbstractQueuedSynchronizer() { }
1. AQS的核心作用
-
提供模板:通过抽象方法(如
tryAcquire
、tryRelease
)定义同步器的核心逻辑,开发者只需实现这些方法即可自定义锁或同步组件。 -
管理线程排队:内部通过 CLH队列(一种FIFO的双向链表)实现线程的阻塞和唤醒机制,处理竞争资源的线程排队问题。
-
支持独占/共享模式:
-
独占模式(Exclusive):同一时刻只有一个线程能获取资源(如
ReentrantLock
)。 -
共享模式(Shared):多个线程可同时获取资源(如
Semaphore
)。
-
2. AQS的核心结构
如下图所示:
-
状态变量(
state
):
通过volatile int state
表示资源状态(如锁的重入次数、信号量的许可证数量)。 -
CLH队列:
存储等待线程的队列,节点(Node
)封装线程及等待状态(如CANCELLED
、SIGNAL
)。
3. 关键方法
-
资源获取:
-
acquire(int arg)
:独占模式下获取资源,内部调用tryAcquire
(需子类实现)。 -
acquireShared(int arg)
:共享模式下获取资源,内部调用tryAcquireShared
。
-
-
资源释放:
-
release(int arg)
:独占模式释放资源,调用tryRelease
。 -
releaseShared(int arg)
:共享模式释放资源,调用tryReleaseShared
。
-
-
模板方法(需子类实现):
protected boolean tryAcquire(int arg); // 独占模式尝试获取资源
protected boolean tryRelease(int arg); // 独占模式尝试释放资源
protected int tryAcquireShared(int arg); // 共享模式尝试获取资源
protected boolean tryReleaseShared(int arg); // 共享模式尝试释放资源
4. AQS的应用示例
4.1、ReentrantLock的实现
-
state
表示锁的重入次数:-
线程首次获取锁时,
state
从0变为1。 -
同一线程重入时,
state
递增。
-
-
非公平锁直接尝试CAS修改
state
,失败后进入队列。
示例1:自定义独占锁(类似ReentrantLock)
import java.util.concurrent.locks.AbstractQueuedSynchronizer;public class MyLock {private final Sync sync = new Sync();// 自定义同步器,继承AQSprivate static class Sync extends AbstractQueuedSynchronizer {// 尝试获取锁(state=0表示未锁定,1表示已锁定)@Overrideprotected boolean tryAcquire(int arg) {if (compareAndSetState(0, 1)) { // CAS操作抢锁setExclusiveOwnerThread(Thread.currentThread()); // 设置当前线程为锁持有者return true;}return false;}// 尝试释放锁@Overrideprotected boolean tryRelease(int arg) {if (getState() == 0) throw new IllegalMonitorStateException();setExclusiveOwnerThread(null); // 清空持有线程setState(0); // 状态置为0return true;}// 是否被当前线程独占@Overrideprotected boolean isHeldExclusively() {return getState() == 1 && getExclusiveOwnerThread() == Thread.currentThread();}}// 对外暴露的加锁方法public void lock() {sync.acquire(1); // 调用AQS的acquire模板方法}// 对外暴露的解锁方法public void unlock() {sync.release(1); // 调用AQS的release模板方法}
}// 测试代码
public class TestMyLock {private static int count = 0;private static MyLock lock = new MyLock();public static void main(String[] args) throws InterruptedException {Runnable task = () -> {for (int i = 0; i < 10000; i++) {lock.lock();try {count++;} finally {lock.unlock();}}};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();t1.join();t2.join();System.out.println("Final count: " + count); // 正确输出20000}
}
关键点:
-
tryAcquire
和tryRelease
是AQS要求子类实现的模板方法。 -
通过CAS操作(
compareAndSetState
)保证原子性。 -
acquire
和release
是AQS提供的模板方法,内部会处理线程排队和唤醒。
4.2、CountDownLatch的实现
-
state
初始化为计数器的值(如N)。 -
await()
调用acquireShared
,当state=0
时唤醒所有等待线程。 -
countDown()
调用releaseShared
,递减state
。
示例2:自定义CountDownLatch
import java.util.concurrent.locks.AbstractQueuedSynchronizer;public class MyCountDownLatch {private final Sync sync;public MyCountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException();this.sync = new Sync(count);}// 自定义同步器(共享模式)private static class Sync extends AbstractQueuedSynchronizer {Sync(int count) {setState(count); // 初始化状态值}// 尝试获取共享资源(state=0时返回1,否则返回-1表示需要阻塞)@Overrideprotected int tryAcquireShared(int acquires) {return getState() == 0 ? 1 : -1;}// 尝试释放共享资源(每次countDown减1)@Overrideprotected boolean tryReleaseShared(int releases) {for (;;) {int current = getState();if (current == 0) return false; // 已经为0,无需操作int next = current - 1;if (compareAndSetState(current, next)) {return next == 0; // 返回true表示所有线程已释放}}}}public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1); // 调用AQS共享模式获取}public void countDown() {sync.releaseShared(1); // 调用AQS共享模式释放}
}// 测试代码
public class TestMyLatch {public static void main(String[] args) throws InterruptedException {MyCountDownLatch latch = new MyCountDownLatch(2);Runnable task = () -> {System.out.println(Thread.currentThread().getName() + " 完成任务");latch.countDown();};new Thread(task, "线程1").start();new Thread(task, "线程2").start();latch.await(); // 主线程等待System.out.println("所有任务完成!");}
}
关键点:
-
tryAcquireShared
返回1表示成功(state=0
时唤醒所有等待线程),-1表示需要阻塞。 -
tryReleaseShared
通过CAS循环递减state
,直到为0时返回true触发唤醒。
5. AQS的优势
-
复用性:开发者无需从头实现线程排队、阻塞/唤醒机制。
-
灵活性:通过重写模板方法,可快速构建自定义同步器。
-
高性能:基于CAS和volatile变量,减少锁竞争开销。
6. 对比其他同步机制
总结
AQS是Java并发包的基石,理解其原理能更高效地使用
ReentrantLock
、Semaphore
等工具,或在需要时开发自定义同步器。其核心思想是 “模板方法模式 + 资源状态管理 + 线程排队”。
参考文章:
1、谈谈Java多线程离不开的AQS_java aqs-CSDN博客