AQS 是什么?
AQS 是什么?
java.util.concurrent.locks.AbstractQueuedSynchronizer,简称 AQS,是 JUC 同步器的底座。像 ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock、StampedLock(部分)等,都是基于 AQS 搭建的。
AQS 做两件事:
-
管理一个原子状态位
state(volatile int),用 CAS 修改; -
维护一个CLH 同步队列(双向链表)来排队阻塞/唤醒获取同步状态失败的线程(
LockSupport.park/unpark)。
你只需要继承 AQS并覆写少数模板方法(如
tryAcquire/tryRelease等),排队、阻塞、唤醒、公平/非公平、可中断、超时……AQS 都帮你处理好了。
核心概念一览
-
state:同步状态(通常 0=空闲,>0=已占用/剩余许可数)。
-
独占模式(exclusive):一次只允许一个线程持有(如
ReentrantLock)。 -
共享模式(shared):可同时允许多个线程持有(如
Semaphore、CountDownLatch的“门闩剩余数”)。 -
CLH 队列:失败的获取者入队,头节点负责唤醒后继。
-
可重入:独占模式下,持有者再次获取会让
state++(如ReentrantLock)。 -
Condition:基于 AQS 提供的
ConditionObject,实现“在锁内等待/通知”。
获取/释放的大致流程(简化)
以独占为例(acquire):
-
调用方先走子类覆写的
tryAcquire(arg):能拿就拿(CAS 改state,设置独占线程)。 -
拿不到则入队(尾插 CLH 队列)、阻塞(
park)。 -
被前驱唤醒后重试获取;成功后设为队头(“哨兵”),以便继续唤醒下一位。
释放(release):
-
调子类
tryRelease(arg):把state调整(可能到 0)。 -
如果完全释放(
state==0),就唤醒后继(unpark)。
共享模式(acquireShared/releaseShared):
-
tryAcquireShared(arg)返回剩余许可(>=0 表示成功且可能还有余量继续唤醒别人;<0 表示失败需排队)。 -
releaseShared成功后会级联唤醒后继,直到余量耗尽或队列空。
可中断与超时:AQS 提供 acquireInterruptibly、tryAcquireNanos 等变体,内部处理打断与超时逻辑。
公平 vs 非公平
-
公平:按队列先来先服务(入队后排在前面的先获取)。如
new ReentrantLock(true)。 -
非公平:允许新来的线程插队(直接先 CAS 一把),吞吐高、抖动大。默认的
ReentrantLock非公平。
和常见同步器的映射
-
ReentrantLock:独占模式(可重入),
state表示重入次数;tryAcquire检查持有者/CAS;tryRelease递减到 0 时释放。 -
Semaphore:共享模式,
state表示剩余许可;tryAcquireShared用 CAS 扣减,>=0 成功。 -
CountDownLatch:共享模式,
state是计数;await共享获取(计数>0 则排队),countDown共享释放(到 0 唤醒所有等待者)。 -
ReentrantReadWriteLock:读共享/写独占;
state高低位编码读写计数并发。
Condition 怎么来的?
AbstractQueuedSynchronizer 内置 ConditionObject:
-
await():把当前线程从同步队列转移到条件队列并释放锁 → 阻塞等待信号。 -
signal():把条件队列里的节点转回同步队列 → 等待被重新竞争锁。 -
只有持锁线程才能
await/signal,这保证了条件变量的正确使用时序。
手写一个最小不可重入互斥锁(示例)
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.TimeUnit;public class SimpleMutex implements Lock {private static class Sync extends AbstractQueuedSynchronizer {// state: 0=free, 1=held@Overrideprotected boolean tryAcquire(int acquires) {// 不可重入:当前没有人持有时才允许 CAS 抢占if (compareAndSetState(0, 1)) {setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}@Overrideprotected boolean tryRelease(int releases) {if (getState() == 0 || getExclusiveOwnerThread() != Thread.currentThread())throw new IllegalMonitorStateException();setExclusiveOwnerThread(null);setState(0);return true; // 唤醒后继}@Overrideprotected boolean isHeldExclusively() {return getState() == 1 && getExclusiveOwnerThread() == Thread.currentThread();}}private final Sync sync = new Sync();@Override public void lock() { sync.acquire(1); }@Override public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }@Override public boolean tryLock() { return sync.tryAcquire(1); }@Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(time));}@Override public void unlock() { sync.release(1); }@Override public java.util.concurrent.locks.Condition newCondition() {return sync.newConditionObject();}
}
说明:我们只覆写了 tryAcquire/tryRelease/isHeldExclusively,其余的排队/阻塞/唤醒/中断/超时全部由 AQS 兜底。
设计细节与性能点
-
阻塞策略:失败后不会疯狂自旋,AQS 在合适节点调用
park(),被前驱或释放唤醒后再竞争,兼顾性能与公平。 -
虚假唤醒:按经典并发模式,使用者应在
while循环里检查条件(AQS 的 Condition 实现已遵循该约定)。 -
可重入:自行在
tryAcquire中检查“当前线程是否持有”,是则state++;释放时state--到 0 才真正释放。 -
中断/超时:AQS 已提供对应 API 的模板逻辑,覆写只管状态语义。
何时自己写一个 AQS 同步器?
-
你需要自定义并发语义(比如“只允许 N 个并发 + 每个线程最多重入 M 次”这类组合规则);
-
现有的
Lock/Semaphore无法直接覆盖你的场景; -
你希望享受“成熟的排队/阻塞/唤醒/中断/超时/条件队列”但只关心状态判定。
只要明确:状态怎么表示(state)、何时算获取成功/释放成功(tryAcquire/tryRelease),剩下交给 AQS。
常见易错点
-
忘记设置/清除独占线程:独占模式下需要
setExclusiveOwnerThread/null。 -
tryRelease 返回值错:返回
true才会唤醒后继;如果还有重入没释放干净要返回false。 -
共享模式返回值语义:
tryAcquireShared返回 ≥0 表示成功(且可能仍有剩余许可),<0 表示失败。 -
Condition 必须在持锁时 await/signal:否则会
IllegalMonitorStateException。 -
自定义同步器的可见性:
state已volatile,但对其他共享字段的发布要遵循 JMM(在成功获取/释放的路径做好可见性保障)。
速记卡(面试 10 秒版)
AQS = CAS 管状态 + CLH 队列管等待。
覆写tryAcquire/tryRelease(独占)或tryAcquireShared/tryReleaseShared(共享)就能做出自己的同步器。
ReentrantLock/Semaphore/CountDownLatch/RWLock都是它的“皮肤”。
Condition 由ConditionObject提供,await/signal依赖持锁语义。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/961401.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!