AQS通过volatile修饰state保证可见性与有序性,并借助Unsafe的CAS指令实现原子更新,避免锁竞争和上下文切换开销。

AQS(AbstractQueuedSynchronizer)通过 CAS(Compare-And-Swap) 原子地修改 state 字段,避免锁竞争,实现线程安全的状态变更。核心在于:state 是用 volatile 修饰的,并配合 Unsafe.compareAndSwapInt 进行无锁更新,失败时自旋重试。
state 字段的声明与内存语义保障
AQS 中的 state 是一个 volatile int 字段:
volatile 保证两点:
- 所有线程读取到的都是最新值(可见性)
- 禁止对 state 的读写与其他普通变量操作重排序(有序性)
但这不提供原子性——所以不能直接用 state++。AQS 必须依赖底层 CAS 指令完成原子更新。
立即学习“Java免费学习笔记(深入)”;
CAS 更新 state 的典型路径(以 acquire 为例)
比如 ReentrantLock#nonfairTryAcquire 中尝试获取锁:
- 先读当前
getState()(volatile 读) - 判断是否为 0(可获取),或是否是当前线程重入
- 若条件满足,调用
compareAndSetState(0, 1)尝试原子设为 1 - 该方法内部调用
Unsafe.compareAndSwapInt(this, stateOffset, expect, update) - 仅当当前 state 值等于 expect 时才更新成功,否则返回 false,上层决定是否重试或入队
Unsafe 和 stateOffset 的作用
AQS 使用 Unsafe 直接操作内存地址,绕过 JVM 的常规字段访问机制:
-
stateOffset是通过Unsafe.objectFieldOffset在类初始化时计算出的state字段在对象内存中的偏移量 - CAS 操作本质是 CPU 级指令(如 x86 的
cmpxchg),由硬件保证“比较+写入”不可中断 - 即使多个线程同时执行 CAS,也只会有一个成功,其余失败并感知到状态已变,从而避免数据覆盖
为什么不用 synchronized?
同步块会阻塞线程、触发上下文切换,开销大;而 CAS 是乐观策略:
- 多数场景下竞争不激烈,一次 CAS 就能成功,几乎没有额外开销
- 失败后可快速重试或转入等待队列(如 AQS 的 CLH 队列),把阻塞控制权交给上层逻辑
- 避免了传统锁的“忙等”缺陷,也规避了 synchronized 的重量级锁膨胀路径


















