2026/7/10 20:28:02

《Java 100 天进阶之路》第54篇:AQS抽象队列同步器(2026版)

《Java 100 天进阶之路》第54篇:AQS抽象队列同步器(2026版) 第54篇AQS抽象队列同步器2026版系列导航《Java 100 天进阶之路》完整目录 |⬅️ 上一篇第53篇volatile与JMM |➡️ 下一篇第55篇线程池ThreadPoolExecutor待发布️ 本文阅读地图3 分钟速览第53篇搞懂了 JMM 和 volatile本篇深入JUC 并发体系的基石——AQS。掌握了 AQSReentrantLock、Semaphore、CountDownLatch 就全部打通了模块核心问题一句话回答AQS 是什么为什么说它是 JUC 的基石JUC 中所有锁和同步器ReentrantLock、Semaphore、CountDownLatch 等底层都基于 AQS核心三要素AQS 由什么构成volatile int state同步状态CLH 双向队列等待队列模板方法模式控制框架独占模式ReentrantLock 怎么实现state 表示锁被占用次数0空闲0被持有或重入共享模式Semaphore/CountDownLatch 怎么实现state 表示剩余许可数/剩余计数多个线程可同时获取面试最爱问高频考点有哪些见文末 面试 小节文章目录第54篇AQS抽象队列同步器2026版️ 本文阅读地图3 分钟速览一、核心知识点二、生活类比从“餐厅排队叫号”到“VIP 通道”三、AQS 核心三要素3.1 state同步状态3.2 CLH 队列等待线程的“排队系统”3.3 模板方法模式四、独占模式以 ReentrantLock 为例4.1 加锁流程acquire4.2 解锁流程release4.3 ReentrantLock 如何实现可重入五、共享模式以 Semaphore 和 CountDownLatch 为例5.1 Semaphore信号量——控制并发数5.2 CountDownLatch门栓——等待所有线程完成5.3 共享模式获取流程acquireShared六、AQS 在 JUC 中的全景图七、生产级避坑清单八、面试高频考点面试官追问陷阱加分题九、练习题 你的学习进度 下一篇文章预告一、核心知识点AQS 是什么AQSAbstractQueuedSynchronizer抽象队列同步器是java.util.concurrent包JUC的底层基石——它是一个抽象类为构建锁和同步器提供了通用框架。核心设计思想AQS 的目标不是“提供一把具体的锁”而是提供一个可复用的同步器框架——把“排队、阻塞、唤醒”这套通用流程封装好子类只需实现“能不能获取同步状态”这个核心判断逻辑。三大核心要素volatile int state同步状态用 CAS 原子更新CLH 双向队列FIFO存储等待获取同步状态的线程模板方法模式定义获取/释放同步状态的骨架子类只需实现tryAcquire/tryRelease等方法两种同步模式模式特点典型实现独占模式Exclusive同一时刻只有一个线程能获取同步状态ReentrantLock共享模式Shared同一时刻多个线程可以同时获取同步状态Semaphore、CountDownLatch、ReentrantReadWriteLock读锁二、生活类比从“餐厅排队叫号”到“VIP 通道”AQS 就像一个餐厅的“排队叫号系统”state同步状态餐厅的空桌数量——独占模式下只有 1 张桌子0全满1空闲共享模式下有多张桌子state剩余空桌数。CLH 队列门口排队等位的顾客队伍FIFO先来后到。模板方法餐厅统一规定“排队、叫号、入座”的流程但每个分店可以决定“什么情况下叫号”子类实现tryAcquire逻辑。独占模式ReentrantLock就像“VIP 包间”包间只有 1 间一次只能进 1 个人state0/1。同一个 VIP 客人可以多次进入可重入state 累加。其他客人在门口排队包间客人出来后才让下一个进。共享模式Semaphore就像“公共停车场”停车场有 N 个车位stateN。每来一辆车占一个车位state-1车位满了就排队。每走一辆车释放一个车位state1唤醒排队车辆。多辆车可以同时进出互不影响。三、AQS 核心三要素3.1 state同步状态// AQS 核心属性privatevolatileintstate;// 同步状态volatile 保证可见性state 是 AQS 的“大脑”——所有同步逻辑都围绕它展开场景state 含义独占模式ReentrantLock0锁空闲1锁被占用1同一线程重入次数共享模式Semaphore剩余许可数量共享模式CountDownLatch剩余计数未完成线程数3.2 CLH 队列等待线程的“排队系统”AQS 内部维护一个CLH 双向队列Craig, Landin, and Hagersten 队列用于管理等待获取同步状态的线程。队列结构Node 的状态waitStatus状态值含义CANCELLED1线程已取消等待超时或被中断SIGNAL-1后继线程需要被唤醒当前节点释放锁时要唤醒下一个CONDITION-2线程在 Condition 上等待PROPAGATE-3共享模式下需要向后传播释放共享锁时通知所有后继00初始状态入队流程线程获取同步状态失败包装成 Node 节点CAS 设置到队尾进入自旋或阻塞等待3.3 模板方法模式AQS 使用模板方法模式定义了获取/释放同步状态的骨架子类只需实现特定方法子类需实现的方法说明tryAcquire(int arg)独占式尝试获取同步状态tryRelease(int arg)独占式尝试释放同步状态tryAcquireShared(int arg)共享式尝试获取同步状态tryReleaseShared(int arg)共享式尝试释放同步状态isHeldExclusively()当前线程是否独占持有同步状态AQS 提供的模板方法子类直接调用acquire(int arg)独占式获取锁模板方法release(int arg)独占式释放锁模板方法acquireShared(int arg)共享式获取锁模板方法releaseShared(int arg)共享式释放锁模板方法四、独占模式以 ReentrantLock 为例独占模式下同一时刻只有一个线程能持有同步状态。4.1 加锁流程acquirepublicfinalvoidacquire(intarg){if(!tryAcquire(arg)// ① 子类实现尝试获取锁acquireQueued(addWaiter(Node.EXCLUSIVE),arg))// ② 获取失败→入队selfInterrupt();// ③ 中断当前线程}完整流程以非公平锁为例4.2 解锁流程releasepublicfinalbooleanrelease(intarg){if(tryRelease(arg)){// ① 子类实现尝试释放锁Nodehhead;if(h!nullh.waitStatus!0)unparkSuccessor(h);// ② 唤醒后继线程returntrue;}returnfalse;}4.3 ReentrantLock 如何实现可重入// ReentrantLock 的 Sync 子类非公平锁的 tryAcquire 实现简化protectedfinalbooleantryAcquire(intacquires){finalThreadcurrentThread.currentThread();intcgetState();if(c0){// 锁空闲 → CAS 抢锁if(compareAndSetState(0,acquires)){setExclusiveOwnerThread(current);returntrue;}}elseif(currentgetExclusiveOwnerThread()){// 当前线程已持有锁 → 重入stateintnextccacquires;setState(nextc);returntrue;}returnfalse;// 其他线程持有锁 → 获取失败}可重入的关键判断当前线程是否已经是锁的持有者——如果是直接state无需再次竞争。五、共享模式以 Semaphore 和 CountDownLatch 为例共享模式下多个线程可以同时持有同步状态state 的值代表剩余可用资源数。5.1 Semaphore信号量——控制并发数Semaphore 的 state 代表剩余许可数量acquire()state - 1如果 state 0 则成功否则入队等待release()state 1唤醒等待线程适用场景限流——同一时刻最多 N 个线程访问某资源。5.2 CountDownLatch门栓——等待所有线程完成CountDownLatch 的 state 代表剩余计数需要等待完成的线程数countDown()state - 1当 state 0 时唤醒所有等待线程await()如果 state ! 0当前线程阻塞等待适用场景主线程等待多个子任务完成后继续执行。5.3 共享模式获取流程acquireSharedpublicfinalvoidacquireShared(intarg){if(tryAcquireShared(arg)0)// ① 子类实现尝试获取共享锁doAcquireShared(arg);// ② 获取失败→入队}共享模式与独占模式的区别独占模式tryAcquire返回 boolean成功/失败共享模式tryAcquireShared返回 int正数还有剩余资源0刚好获取完负数失败六、AQS 在 JUC 中的全景图七、生产级避坑清单✅ AQS 与同步器使用避坑指南 1. 不要直接使用 AQS → 用它的子类ReentrantLock、Semaphore、CountDownLatch 2. CountDownLatch 计数器不能重置 → 需要重置用 CyclicBarrier 3. Semaphore 获取许可后记得释放 → try-finally 保证 release() 4. 公平锁 vs 非公平锁公平锁保证 FIFO吞吐量更低非公平锁允许插队吞吐量更高 5. ReentrantLock 必须在 finally 中 unlock() → 否则锁永不释放 6. tryLock() 带超时 → 获取失败可以做降级或重试避免线程永久阻塞 7. Condition.await() 必须在 lock() 后调用 → 否则抛 IllegalMonitorStateException八、面试高频考点Q1AQS 是什么核心思想是什么AQS 是 AbstractQueuedSynchronizer 的缩写JUC 并发包的底层基石。核心思想是用一个volatile int state表示同步状态用一个 FIFO 双向队列CLH 队列管理等待线程通过模板方法模式让子类只需实现tryAcquire/tryRelease等少量方法就能构建自定义同步器。Q2AQS 支持哪两种模式分别对应哪些实现独占模式Exclusive同一时刻只有一个线程能获取同步状态典型实现是 ReentrantLock。共享模式Shared同一时刻多个线程可以同时获取同步状态典型实现是 Semaphore、CountDownLatch、ReentrantReadWriteLock 的读锁。Q3ReentrantLock 的可重入性是如何实现的在tryAcquire方法中先判断 state 是否为 0锁空闲如果是则 CAS 抢锁如果 state 0检查当前线程是否已经是锁的持有者如果是则直接state可重入如果不是则获取失败。解锁时每次state--直到 state 归零才真正释放锁。Q4AQS 的 CLH 队列是什么CLH 队列是一个 FIFO 双向链表用于存储等待获取同步状态的线程。每个线程被封装成 Node 节点包含线程引用、等待状态waitStatus、前驱和后继指针。获取同步状态失败的线程会入队等待释放锁时唤醒队首线程。Q5CountDownLatch 和 Semaphore 的区别CountDownLatchstate 表示倒计数countDown()减 1state 归零时唤醒所有等待线程计数器不能重置。Semaphorestate 表示剩余许可数acquire()减 1release()加 1许可数可以动态增减用于限流控制并发访问数。Q6AQS 如何避免 ABA 问题AQS 使用 CAS 操作更新 stateCAS 本身存在 ABA 问题但 AQS 的 state 是整型计数器单调增/减ABA 问题不会影响锁的正确性——state 的值变化代表着“锁被释放了又被获取”对于同步语义来说只要最终状态正确即可。需要 ABA 敏感的原子操作应使用AtomicStampedReference。面试官追问陷阱加分题追问1“tryAcquire和acquire的区别是什么”tryAcquire是子类需要实现的具体逻辑尝试获取同步状态返回 boolean由子类根据 state 判断能否获取。acquire是 AQS 提供的模板方法封装了“尝试获取→失败入队→阻塞等待”的完整流程子类不能重写。追问2“AQS 的共享模式和独占模式在队列处理上有什么区别” 独占模式释放锁时只唤醒一个后继线程unparkSuccessor。共享模式释放锁时会传播唤醒——如果当前节点的waitStatus为PROPAGATE会继续唤醒后继节点让多个等待线程同时获取共享锁。追问3“ReentrantLock的公平锁和非公平锁在 AQS 层面有什么区别” 非公平锁的tryAcquire中CAS 抢锁前不检查CLH 队列中是否有等待线程直接尝试 CAS——这就是“插队”。公平锁的tryAcquire中如果 CLH 队列中有等待线程当前线程直接失败入队不抢锁保证 FIFO 顺序。其他流程入队、阻塞、唤醒完全一致。九、练习题分析题AQS 的 CLH 队列为什么需要设计成双向链表而不是单向链表 思路需要支持取消等待CANCELLED 节点出队单向链表无法从后往前删除同时 Condition 等待队列的节点转移时需要前驱指针。源码题阅读 ReentrantLock 的非公平锁tryAcquire源码画出加锁失败时线程入队和阻塞的完整流程。场景设计某系统需要对数据库连接池做限流最多同时允许 10 个线程获取连接。用 Semaphore 如何实现 思路Semaphore semaphore new Semaphore(10);获取连接前semaphore.acquire()归还连接时semaphore.release()用 try-finally 保证释放。 你的学习进度当前第54篇 / 共108篇 ·进阶篇并发编程与JUC详解第51~60篇✅ 已完成基础篇44篇 第45~54篇 正在学第54篇⏳ 待学习第55~108篇 完整目录 学习指南 | 订阅本专栏不错过每一篇 下一篇文章预告下一篇《第55篇线程池ThreadPoolExecutor》内容简介七大参数深度解析、四种拒绝策略、任务队列选型、动态调优、Executors工厂方法的陷阱无界队列 OOM、实际生产案例。多线程专题持续深入拿下线程池核心《Java 100 天进阶之路 | 从入门到上岗就业》每天一篇建议收藏 关注一起100天拿offer 点击关注我更新后第一时间收到推送