深入理解 AbstractQueuedSynchronizer (AQS):Java 并发的排队管家

最近在准备面试,正把平时积累的笔记、项目中遇到的问题与解决方案、对核心原理的理解,以及高频业务场景的应对策略系统梳理一遍,既能加深记忆,也能让知识体系更扎实,供大家参考,欢迎讨论。

AbstractQueuedSynchronizer 就是:

👉 "一个抽象的、基于队列的同步器"。

  • 抽象:自己不能直接用,得继承。
  • 排队:管理等待线程的队列。
  • 同步器:用于实现各种同步工具(可重入锁、信号量、闭锁...)。

在 Java 并发编程里,虽然平时我们写代码常用的是 ReentrantLock(独占锁)、CountDownLatch(共享锁)、Semaphore,但它们的底层都是靠 AQS 实现的。


1. AQS 是什么?

一句话:

👉 AQS 就是一个排队管家。

  • state :state 是个抽象的同步状态,不同场景下有不同解释。
    • 在 ReentrantLock 里,state 表示 锁的重入次数 (0 代表未加锁,>0 表示已加锁,并统计重入层数)。
    • 在 CountDownLatch 里,state 表示 计数值(倒计时还有几个)。
    • 在 Semaphore 里,state 表示 剩余的许可数量。
  • 队列 :没抢到资源的线程,会被放进一个 FIFO 队列。
  • 阻塞与唤醒 :没轮到的线程,先 park(睡觉);轮到时,再 unpark(叫醒)。

AQS 不管具体规则,只提供排队机制。子类自己定义"怎么抢""怎么放",AQS 负责排队+调度"。


2. 核心流程

拿独占锁为例:

  1. 线程调用 lock(),底层走到 AQS 的 acquire()。

  2. tryAcquire():尝试直接抢锁(子类自己实现)。

  3. 如果抢不到:

    • 把当前线程打包成 Node → 入队。
    • 调用 LockSupport.park() 把线程挂起。
  4. 当持有锁的线程 unlock():

    • AQS 调用 tryRelease() 释放资源。
    • 唤醒队列里下一个节点的线程。
  5. 被唤醒的线程重新去尝试抢锁。


3. ReentrantLock 如何用 AQS?

ReentrantLock 就是基于 AQS 实现的独占锁(独占模式),它的实现类 Sync 继承了 AQS,并重写了关键方法:

  • tryAcquire(int arg):如果没人占用锁,或者自己重入 → 修改 state + 1 并返回 true。
  • tryRelease(int arg): state - 1,若为 0 表示锁完全释放。

剩下的排队、阻塞、唤醒,全由 AQS 管理。


4. CountDownLatch 如何用 AQS?

CountDownLatch 基于 AQS 的 共享模式(Shared) 实现,它的内部类 Sync 继承了 AQS,并重写了关键方法:

  • tryAcquireShared(int arg):检查 state(计数器)是否为 0,如果为 0 → 返回正值,表示可以获取;否则返回负值,表示线程需要排队等待。
  • tryReleaseShared(int arg):原子将 state 减 1,如果减到 0 → 返回 true,AQS 会唤醒所有等待线程。

剩下的排队、阻塞、唤醒,全由 AQS 管理。

很好的抽象,该交给子类去扩展的就封装抽象方法,让子类决定是否支持共享模式


相关推荐
小羊没烦恼!2 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里2 天前
java中的继承和多态的区别
java
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕2 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码2 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖2 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时3 天前
01-06-A-JVM排查实战详解
java·jvm
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
vipxieliang3 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++