JUC包里的同步组件主要实现了AQS的哪些主要方法
要理解 JUC(java.util.concurrent)包中同步组件的底层逻辑,核心是它们都基于AbstractQueuedSynchronizer(AQS)框架——AQS 提供了通用的同步状态管理、CLH 队列(等待队列)和模板方法,而具体组件只需重写 AQS 的关键抽象方法,即可实现自定义的同步语义。
一、先明确 AQS 的核心角色
AQS 是一个抽象基类,定义了同步组件的通用结构:
- 同步状态(
state):用volatile int表示同步状态(如锁的重入次数、计数器的剩余值)。 - CLH 队列:双向链表实现的等待队列,存储未获取到同步状态的线程。
- 节点状态(
waitStatus):表示节点的等待状态(如CANCELLED、SIGNAL、CONDITION等)。 - 模板方法:如
acquire(int arg)(独占获取)、release(int arg)(独占释放)、acquireShared(int arg)(共享获取)、releaseShared(int arg)(共享释放)——这些方法调用需要子类重写的抽象方法完成具体逻辑。
二、JUC 组件重写的 AQS 核心方法
不同 JUC 组件(如锁、计数器、信号量)对应独占模式或共享模式,因此重写的 AQS 方法也不同。以下是典型组件的实现分析:
1. 独占模式组件(如 ReentrantLock、ThreadPoolExecutor.Worker)
独占模式用于“一次只有一个线程持有资源”的场景,需重写:
tryAcquire(int arg):尝试获取独占同步状态(成功返回true,失败返回false)。tryRelease(int arg):尝试释放独占同步状态(成功返回true,失败返回false)。isHeldExclusively():判断当前线程是否独占同步状态(用于条件变量)。
示例 1:ReentrantLock(可重入独占锁)
ReentrantLock内部通过 Sync抽象类继承 AQS,再派生出 NonfairSync(非公平锁)和 FairSync(公平锁),重写以下方法:
tryAcquire(int acquires):尝试获取锁(
acquires=1表示获取一次锁)。- 非公平锁:直接通过 CAS 修改
state(若state==0则获取成功,否则判断是否是当前线程重入)。 - 公平锁:先检查 CLH 队列中是否有前驱节点(保证先到先得),再尝试 CAS 修改
state。
// NonfairSync 的 tryAcquire protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (compareAndSetState(0, acquires)) { // CAS 抢锁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; }- 非公平锁:直接通过 CAS 修改
tryRelease(int releases):释放锁(
releases=1表示释放一次)。减少state,若state变为 0,则彻底释放锁,并设置exclusiveOwnerThread为null。protected final boolean tryRelease(int releases) {int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free; }isHeldExclusively():判断当前线程是否持有锁(用于Condition的等待队列)。
示例 2:ThreadPoolExecutor.Worker(工作线程的独占锁)
Worker继承 AbstractQueuedLongSynchronizer(AQS 的长整型版本),用于实现不可重入的独占锁:
tryAcquire(long arg):只有当前线程是Worker自身时才能获取锁(防止并发修改任务)。tryRelease(long arg):释放锁(将state置 0)。
2. 共享模式组件(如 CountDownLatch、Semaphore、CyclicBarrier)
共享模式用于“多个线程可同时持有资源”的场景,需重写:
tryAcquireShared(int arg):尝试获取共享同步状态(返回>=0表示成功,<0表示失败)。tryReleaseShared(int arg):尝试释放共享同步状态(成功返回true,失败返回false)。getSharedQueuedThreads()(可选):获取等待队列中的共享线程(用于监控)。
示例 1:CountDownLatch(闭锁)
CountDownLatch内部通过 Sync继承 AQS,state初始化为计数器 count:
tryAcquireShared(int acquires):尝试获取共享锁(即判断计数器是否为 0)。若
state == 0,返回1(成功);否则返回-1(失败,进入等待队列)。protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1; }tryReleaseShared(int releases):释放共享锁(减少计数器)。通过 CAS 循环将
state减 1,若state变为 0,则唤醒所有等待的共享线程。protected boolean tryReleaseShared(int releases) {for (;;) {int c = getState();if (c == 0) return false; // 已经是 0,无需释放int nextc = c - 1;if (compareAndSetState(c, nextc))return nextc == 0; // 若减到 0,返回 true 唤醒其他线程} }
示例 2:Semaphore(信号量)
Semaphore内部通过 Sync继承 AQS,state表示可用许可数:
tryAcquireShared(int acquires):尝试获取
acquires个许可(若剩余许可足够,则 CAS 减少许可并返回成功;否则失败)。protected int tryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 || compareAndSetState(available, remaining))return remaining;} }tryReleaseShared(int releases):释放
releases个许可(CAS 增加许可,若成功则唤醒等待线程)。
3. 双模式组件(如 ReentrantReadWriteLock)
ReentrantReadWriteLock同时支持读锁(共享)和写锁(独占),内部通过两个 Sync子类实现:
- 写锁(
WriteLock):继承Sync,重写tryAcquire和tryRelease(独占逻辑)。tryAcquire:判断是否有其他线程持有写锁,或当前线程是否可重入。
- 读锁(
ReadLock):继承Sync,重写tryAcquireShared和tryReleaseShared(共享逻辑)。tryAcquireShared:判断是否有写锁持有,或当前线程是否已持有读锁(可重入)。
4. 条件变量(ConditionObject)
ConditionObject是 AQS 的内部类,实现 Condition接口,依赖 AQS 的条件队列(单向链表):
await():释放当前线程的同步状态,将节点从同步队列移到条件队列,进入等待。signal():将条件队列中的第一个节点移回同步队列,唤醒该线程。signalAll():将条件队列中的所有节点移回同步队列,唤醒所有线程。
三、总结:JUC 组件与 AQS 方法的对应关系
组件类型 | 典型组件 | 模式 | 重写的 AQS 核心方法 |
|---|---|---|---|
独占锁 |
| 独占 |
|
独占锁 |
| 独占 |
|
共享锁 |
| 共享 |
|
共享锁 |
| 共享 |
|
双模式锁 |
| 独占+共享 | 写锁: |
条件变量 |
| —— | 依赖 AQS 的条件队列实现 |
四、关键结论
JUC 组件的底层逻辑可总结为:
AQS 提供通用框架(状态管理、队列、模板方法),具体组件通过重写 tryAcquire/tryRelease(独占)或 tryAcquireShared/tryReleaseShared(共享)实现自定义同步语义。
这种设计让 JUC 组件无需重复造轮子,只需聚焦于“如何获取/释放同步状态”,即可快速实现高效、可靠的同步工具。
