aqs 通过 volatile state 字段与内存屏障保障可见性:volatile 读写分别插入 loadload/loadstore 和 storestore/storeload 屏障,建立 happens-before 关系,并借助 lock 指令和缓存一致性协议实现与 synchronized 等价的内存语义。

AQS 底层通过 volatile state 字段 与配套的内存屏障协同工作,保障状态修改对所有线程的可见性。这不是靠“自动同步”或“魔法刷新”,而是 JVM 在读写 state 时,严格插入特定屏障,强制工作内存与主内存按序交互,并借助硬件缓存一致性协议(如 MESI)落地。
volatile state 是可见性的锚点
AQS 的核心状态字段 private volatile int state 是整个同步机制的可见性枢纽:
- 每次调用
getState()(如尝试获取锁、检查条件)都触发一次 volatile 读,JVM 插入 LoadLoad + LoadStore 屏障:确保该读之前的所有读已完成,并禁止后续写被重排到它前面;同时强制清空本地缓存中state对应的缓存行,从主内存或其它核心缓存拉取最新值。 - 每次调用
setState(int)(如成功加锁设为 1,或解锁设为 0)都触发一次 volatile 写,JVM 插入 StoreStore + StoreLoad 屏障:保证该写之前所有普通写(如临界区内的变量更新)已刷新到主内存;并阻止后续读/写重排到该写之前,同时广播缓存失效信号,使其他 CPU 核心丢弃旧副本。
屏障组合建立 happens-before 关系
内存屏障本身不直接传递语义,而是支撑 JMM 定义的 happens-before 规则:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 线程 A 调用
unlock()时,对state的 volatile 写构成一个“同步释放”动作; - 线程 B 后续调用
lock()时,对state的 volatile 读构成一个“同步获取”动作; - JMM 规定:同一锁实例上,前者的 volatile 写 happens-before 后者的 volatile 读;
- 由此推导出:线程 A 在
unlock()前对任意共享变量(如count、data[])的所有写操作,对线程 B 在lock()后的读操作全部可见。
硬件层由 LOCK 指令兜底
JVM 在 x86 平台实现 volatile 写时,通常生成带 LOCK 前缀的指令(如 lock addl $0x0, (%rsp)):
- 该指令强制将当前 CPU 写缓冲区中所有待刷新数据(不限于
state)一次性刷入缓存或主内存; - 同时触发总线嗅探(bus snooping),使其他 CPU 缓存中对应缓存行置为 Invalid 状态;
- 因此,
unlock()不是只同步state,而是把整个临界区结束时的全部脏数据“一揽子”同步出去。
与 synchronized 语义完全对齐
Java 规范明确要求 Lock 实现必须提供与内置 monitor 锁等价的内存语义:
- 临界区内修改的非 volatile 变量,无需额外加 volatile 修饰;
- 加锁入口和解锁出口天然带有内存屏障效应(进入插 LoadLoad+LoadStore,退出插 StoreStore+StoreLoad);
- AQS 的设计正是将这一语义精准映射到 volatile + CAS + 队列唤醒的组合中,不依赖 synchronized 的字节码级锁指令,但效果一致。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










