Skip to content

说说 AQS 吧? ​

  • 标签:Java并发、Java

🏛 业务架构师 ​

AQS(AbstractQueuedSynchronizer)是 J.U.C 的地基:一个 volatile int state + 一条 CLH 变体的 FIFO 等待队列,加锁就是 CAS 改 state,失败就封装成节点入队、park 挂起;释放时唤醒队头继任者。ReentrantLock、Semaphore、CountDownLatch、读写锁全是基于它实现——换的是 state 的语义,不换的是队列骨架。

业务视角我讲"怎么按业务语义选对 AQS 工具",这才是日常用得上的:

  • CountDownLatch(一次性倒计时):并行调多个下游后聚合——详情页同时取商品、库存、优惠券三个服务,await 等齐再返回,P99 从串行 600ms 变并行 200ms。注意它不可重置,"发起-等待"一次性场景专用;
  • CyclicBarrier(可重置栅栏):多线程分段计算、到齐后统一汇总再下一轮——批处理分片计算用得上;
  • Semaphore(配额):限制并发名额——调用昂贵第三方(按并发计费的风控接口)限 10 个并发,保护配额即保护成本;连接池限流同思路;
  • ReentrantReadWriteLock:读多写少的本地缓存(配置表、字典表),读并行写互斥。

两条纪律:优先用这些"标准件"而不是手写 wait/notify(正确性难保证、易错且难维护);等待要带超时(tryAcquire 带 timeout),挂死线程的排查成本远高于多写一行超时参数。

一句话:AQS 是引擎,工具类是按业务语义造好的车——业务工程师选车,不必造引擎,但要懂每辆车适合什么路。

🏗 系统架构师 ​

AQS(AbstractQueuedSynchronizer)是 J.U.C 的同步器骨架,ReentrantLock、Semaphore、CountDownLatch、线程池的 Worker 全构建在它上面。我从三要素讲:

  • state(volatile int):同步状态的唯一载体,语义由子类定义——ReentrantLock 里是重入次数,Semaphore 是剩余许可,CountDownLatch 是未完成的计数;
  • CLH 变体等待队列:获取失败的线程封装成 Node 入双向队列(FIFO),Node 的 waitStatus 马丁级精简(SIGNAL/CANCELLED/CONDITION),线程在此 park 挂起;
  • 模板方法模式:子类只需实现 tryAcquire/tryRelease(独占)或 tryAcquireShared/tryReleaseShared(共享),排队、阻塞、唤醒、取消的复杂逻辑全部由 AQS 骨架承担——这是"不变与变化分离"的教科书设计。

核心流程(以非公平锁为例):lock → CAS 修改 state(先插队尝试)→ 失败入队 → 前驱是 head 再尝试 → 仍失败 park;释放时 unpark 队列后继。公平与非公平的差异只是获取时是否检查队列中有前驱——非公平吞吞吐(减少唤醒延迟),公平保序防饥饿。

我的实践应用:基于 AQS 做过"并发数受限的任务分发器"和读写分离锁;面试更深的一层是理解它和 Morton 条件、LockSupport 的协作,这是读 J.U.C 源码的钥匙。

💻 全栈开发专家 ​

AQS(AbstractQueuedSynchronizer)= JUC 的锁/同步器骨架,一句话:一个 volatile int state + 一条 CLH 变体的 FIFO 等待队列。

三大件:

  1. state:同步语义的载体。ReentrantLock 里 state=重入次数;Semaphore 里=剩余许可;CountDownLatch 里=未完成计数。对 state 的修改都是 CAS。
  2. 等待队列:获取失败的线程封装成 Node 入队,LockSupport.park() 挂起;前驱释放时 unpark 唤醒后继。Node 里 waitStatus 维持唤醒链。
  3. 模板方法:AQS 骨架管排队/挂起/唤醒(acquire/release),怎么算"获取成功"交给子类:
java
// 独占式获取的骨架流程
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&                    // ① 子类实现:CAS state
        acquireQueued(node, arg))              // ② 失败入队 park
        selfInterrupt();
}

子类实现就两个方法:tryAcquire/tryRelease(独占)或 tryAcquireShared(共享)。

基于 AQS 的全家桶:ReentrantLock(公平=先排队,非公平=先抢后排队,吞吐优先非公平)、Semaphore、CountDownLatch(共享式,state 减到 0 唤醒所有)、CyclicBarrier(ReentrantLock+Condition 实现)、线程池 Worker 也继承 AQS(防中断时重入)。

手写过的例子:一个不允许并发的"互斥执行器"只需 30 行 tryAcquire/tryRelease——理解 AQS 后,JUC 一半的类就通了。