【Java源码阅读系列23】深度解读Java AbstractQueuedSynchronizer(AQS)源码

AbstractQueuedSynchronizerAQS)是 Java java.util.concurrent.locks 包中的核心同步框架,被广泛用于实现 ReentrantLockReentrantReadWriteLockCountDownLatch 等并发工具。本文将结合 JDK 8 源码,从核心设计、关键组件、设计模式等角度深入解析其实现原理。

一、AQS 的定位与核心目标

AQS 是 Java 并发编程的“基础设施”,其核心目标是通过统一的框架简化自定义同步工具的实现。开发者只需继承 AQS 并实现几个抽象方法(如 tryAcquiretryRelease),即可快速构建独占锁、共享锁、信号量等同步工具,而无需关心底层的队列管理、线程阻塞/唤醒等复杂逻辑。


二、核心组件:状态、队列与节点

AQS 的核心能力由以下三个组件支撑:

1. 同步状态:volatile int state

AQS 通过一个 volatile int 类型的 state 字段表示同步状态。其语义由子类定义:

  • ReentrantLockstate 表示锁的重入次数(0 表示未锁定)。
  • CountDownLatchstate 表示计数器的剩余值(初始为 N,减到 0 时释放所有等待线程)。
  • Semaphorestate 表示可用许可的数量。

AQS 提供了 getState()setState()compareAndSetState() 方法操作 state,其中 compareAndSetState() 基于 UnsafeCASCompare-And-Swap)实现原子性修改,保证多线程下的可见性和原子性。

2. 等待队列:CLH 变体的双向链表

AQS 的等待队列是 CLHCraig-Landin-Hagersten)锁队列的变体,用于管理未获取到同步状态的线程。CLH 队列原本是为自旋锁设计的单向链表,AQS 将其改进为双向链表,以支持更灵活的线程取消和唤醒操作。

队列的核心字段

  • head:队列头节点(懒加载,初始为 null),表示当前持有同步状态的线程。
  • tail:队列尾节点,新线程入队时通过 CAS 原子性地更新 tail

队列中的每个节点(Node)记录了:

  • volatile Thread thread:当前等待的线程。
  • volatile Node prev/next:前驱和后继节点(双向链表)。
  • volatile int waitStatus:线程状态(如 SIGNALCANCELLED 等)。

3. 节点状态:Node.waitStatus

waitStatus 字段用于标记节点的状态,支持以下取值:

  • CANCELLED(1):线程因超时或中断被取消(永久状态)。
  • SIGNAL(-1):后继节点需要被唤醒(当前节点释放时需通知后继)。
  • CONDITION(-2):节点在条件队列中等待(与 ConditionObject 相关)。
  • PROPAGATE(-3):共享模式下,释放操作需传播给后续节点。
  • 0:初始状态或无特殊含义。

通过 waitStatusAQS 可以高效管理线程的阻塞与唤醒,避免无意义的自旋或阻塞。


三、核心流程:获取与释放同步状态

AQS 的核心逻辑围绕“获取同步状态”和“释放同步状态”展开,支持独占模式(如 ReentrantLock)和共享模式(如 CountDownLatch)。

1. 独占模式:以 acquire(int arg) 为例

独占模式的获取流程如下(以 ReentrantLock 为例):

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&  // 子类实现:尝试获取同步状态(如锁)
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  // 入队并阻塞
        selfInterrupt();
}
  • tryAcquire(arg):子类实现的核心方法,通过 CAS 修改 state 尝试获取同步状态(如 ReentrantLock.Sync.tryAcquire 检查 state 是否为 0 或当前线程重入)。
  • addWaiter(Node.EXCLUSIVE):若获取失败,创建独占模式节点并加入队列尾部。
  • acquireQueued(node, arg):循环检查前驱节点是否为头节点(尝试再次获取),若失败则阻塞当前线程(通过 LockSupport.park),直到被唤醒或中断。

2. 共享模式:以 acquireShared(int arg) 为例

共享模式允许多个线程同时获取同步状态(如 CountDownLatch):

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)  // 子类实现:尝试获取共享状态(如计数器是否为 0)
        doAcquireShared(arg);  // 入队并阻塞
}
  • tryAcquireShared(arg):子类实现,返回值表示获取结果(负数表示失败,0 表示成功但无后续可能,正数表示成功且允许后续获取)。
  • doAcquireShared(arg):与独占模式类似,但唤醒时需传播释放操作(通过 setHeadAndPropagate),确保后续共享节点也能被唤醒。

3. 释放同步状态:以 release(int arg) 为例

释放同步状态的流程如下(以 ReentrantLock 为例):

public final boolean release(int arg) {
    if (tryRelease(arg)) {  // 子类实现:尝试释放同步状态(如减少重入次数)
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);  // 唤醒头节点的后继线程
        return true;
    }
    return false;
}
  • tryRelease(arg):子类实现,释放同步状态(如 ReentrantLock.Sync.tryRelease 减少 state 并检查是否完全释放)。
  • unparkSuccessor(h):唤醒头节点的后继线程(通过 LockSupport.unpark),使其重新尝试获取同步状态。

四、设计模式分析

AQS 的实现中隐含了三种关键设计模式,体现了其高扩展性和灵活性。

1. 模板方法模式(Template Method Pattern)

AQS 最核心的设计模式是模板方法模式。AQS 定义了获取/释放同步状态的骨架流程(如 acquirerelease),但将具体的同步逻辑(tryAcquiretryRelease 等)留给子类实现。
示例

  • acquire(int arg) 方法定义了“尝试获取→失败则入队阻塞”的通用流程,而 tryAcquire(arg)ReentrantLock.Sync 子类实现,根据锁的重入逻辑检查 state

优点:复用公共逻辑(队列管理、线程阻塞),子类只需关注具体同步逻辑,降低了自定义同步工具的复杂度。

2. 观察者模式(Observer Pattern)

等待队列中的节点通过前驱节点的状态变化被唤醒,类似于观察者模式中“被观察对象通知观察者”的机制。

  • 被观察对象:前驱节点(释放同步状态时)。
  • 观察者:后继节点(需要被唤醒以重新尝试获取)。

示例
当前驱节点释放锁(release)时,通过 unparkSuccessor 唤醒后继节点(观察者),使其从阻塞中恢复。

优点:解耦线程的阻塞与唤醒逻辑,通过队列的链式结构高效传递唤醒信号。

3. 策略模式(Strategy Pattern)

AQS 支持通过子类实现不同的同步策略(如公平锁 vs 非公平锁),符合策略模式“算法族可替换”的思想。
示例
ReentrantLock FairSync(公平锁)和 NonfairSync(非公平锁)均继承自 SyncAQS 的子类),分别实现 tryAcquire 方法:

  • 公平锁检查队列中是否有前驱线程(hasQueuedPredecessors),确保先到先得。
  • 非公平锁直接尝试 CAS获取锁(允许“插队”),提高吞吐量。

优点:允许在运行时动态选择同步策略(通过构造函数参数),增强了灵活性。


五、AQS 的扩展:ConditionObject

AQS 内置了 ConditionObject 类,用于实现条件变量(类似 Object.wait()/notify())。条件变量支持更精细的线程等待/唤醒控制,例如:

  • 线程在条件不满足时调用 condition.await() 进入条件队列。
  • 当条件满足时,其他线程调用 condition.signal() 将其转移到同步队列,重新竞争锁。

ConditionObject 的核心逻辑

  • 条件队列:通过 Node.nextWaiter 链接的单向链表,存储等待条件的线程。
  • await():释放当前锁,将线程加入条件队列并阻塞。
  • signal():将条件队列的头节点转移到同步队列,唤醒线程重新竞争锁。

六、AQS 的设计哲学与意义

AQS 的设计哲学可总结为:通过封装底层队列管理和线程控制,将复杂的同步逻辑简化为几个抽象方法的实现。其意义在于:

  • 降低开发门槛:开发者无需手动实现队列、CAS、线程阻塞等底层操作,专注于业务逻辑。
  • 保证一致性:所有基于 AQS 的同步工具(如锁、信号量)具有统一的行为(如可中断、可超时)。
  • 高性能:通过 CLH 队列的变体和 CAS 操作,减少线程上下文切换,提升并发效率。

总结

AbstractQueuedSynchronizer 是 Java 并发编程的“基石”,其通过状态管理、队列控制和模板方法模式,为各种同步工具提供了统一的实现框架。理解 AQS 的源码,不仅能深入掌握 ReentrantLock 等工具的底层逻辑,更能学会如何设计高效、可扩展的多线程同步机制。掌握 AQS,是从“使用并发工具”到“设计并发工具”的关键一步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值