Java 开发中高频使用的 ReentrantLock、CountDownLatch、Semaphore、CyclicBarrier 等JUC并发工具,全部构建在同一个底层核心之上——AbstractQueuedSynchronizer(抽象队列同步器,简称AQS)。它是Doug Lea设计的通用同步框架,统一了JUC体系的同步实现逻辑,是Java并发进阶必须吃透的底层核心。
一、AQS的核心设计思想
AQS用极简的两个核心组件,解决了并发同步的两大根本问题:同步状态的原子管理、竞争失败线程的排队与唤醒。
- volatile修饰的int型state同步状态变量
这是AQS的同步核心,所有线程对同步资源的抢占,本质都是对state变量的原子操作。volatile保证了state的多线程可见性,所有修改均通过CAS实现原子性,彻底规避了竞态问题。 - CLH双向链表实现的线程等待队列
竞争state失败的线程,会被封装成Node节点加入这个双向队列,进入阻塞等待状态;当持有同步状态的线程释放资源时,会按队列顺序唤醒后继线程,实现公平的排队调度,避免了CPU空转与线程惊群效应。
二、两大核心工作模式
AQS基于模板方法模式设计,将线程排队、阻塞、唤醒等通用逻辑全部封装固化,开发者仅需实现少量try开头的模板方法,自定义state的获取与释放规则,即可快速实现自定义同步工具。它支持两种完全不同的同步模式,覆盖了几乎所有并发场景。
- 独占模式
同一时间仅允许一个线程持有同步状态,典型实现是ReentrantLock。核心模板方法为tryAcquire(尝试获取独占资源)与tryRelease(尝试释放独占资源)。
可重入锁的实现逻辑,就是在tryAcquire中判断当前线程是否为锁持有线程,若是则对state做累加,释放时递减至0才真正释放锁,整个逻辑完全由子类自定义,AQS不做任何限制。 - 共享模式
同一时间允许多个线程持有同步状态,典型实现是CountDownLatch、Semaphore。核心模板方法为tryAcquireShared与tryReleaseShared。
例如Semaphore就是将state作为许可总数,acquire时对state做减法,release时做加法,实现了限流的核心逻辑;CountDownLatch则是将state作为倒计时数,计数归0后唤醒所有等待线程。
三、底层核心优化细节
- 线程阻塞与唤醒机制
AQS全程使用LockSupport.park()/unpark()实现线程的阻塞与唤醒,相比传统的wait/notify机制,它不会出现唤醒丢失问题——unpark()可以先于park()调用,提前为线程颁发运行许可,彻底规避了同步时序导致的线程永久阻塞问题。 - 节点等待状态管控
队列中的每个Node节点都维护了waitStatus等待状态,标记线程的运行状态,核心包括SIGNAL(后继线程需要被唤醒)、CANCELLED(线程已取消等待)等,AQS通过状态精准控制线程的唤醒时机,避免无效的唤醒操作与CPU空耗。 - 公平与非公平调度原生支持
AQS天然支持公平与非公平两种调度策略:公平锁在抢占资源前,会先检查等待队列是否有排队线程,有则直接进入队列排队;非公平锁则会直接通过CAS抢占资源,抢占失败再进入队列,这也是非公平锁吞吐量更高的核心原因。
四、核心认知误区与最佳实践
- 常见误区1:AQS就是锁。真相:AQS是通用同步框架,锁只是它的一种应用场景,它还能实现倒计时门闩、信号量、栅栏、限流器等各类同步工具,适用范围远大于锁本身。
- 常见误区2:AQS实现的锁性能不如
synchronized。真相:JDK1.6+之后,基于AQS的ReentrantLock在高竞争长耗时场景下,性能优于升级到重量级的synchronized,同时还提供了可中断、超时获取、公平锁切换等更灵活的能力。 - 常见误区3:AQS仅能用于JUC内置工具。真相:我们可以通过继承AQS,快速实现自定义高性能同步组件,比如分布式锁的本地排队层、自定义限流器、多线程协同屏障等,仅需实现对应的模板方法即可,无需重复开发线程排队等复杂逻辑。
结语
AQS是Java JUC并发体系的灵魂,它用极简的设计,将复杂的并发底层逻辑封装成通用能力,让开发者无需关注线程调度的细节,仅需聚焦业务同步规则的实现。理解AQS的底层原理,不仅能彻底搞懂JUC常用工具的实现逻辑,更是排查并发死锁、性能瓶颈,甚至实现自定义高性能同步组件的核心前提。