reentrantlock 通过 aqs 的 volatile state 字段、happens-before 规则及内存屏障,确保锁操作前后所有共享变量的可见性;其语义与 synchronized 一致,临界区内变量无需额外 volatile 修饰。

ReentrantLock 通过 AQS(AbstractQueuedSynchronizer)中 volatile 修饰的 state 字段,结合 Java 内存模型(JMM)定义的 happens-before 规则和底层内存屏障机制,来保证锁获取操作的内存可见性。
volatile state 是可见性的核心载体
ReentrantLock 的加锁(lock())和解锁(unlock())最终都操作 AQS 的 state 字段,该字段被声明为 volatile int state。这意味着:
- 每次读取
state(如尝试获取锁时的getState())都会触发“读 volatile 变量”动作,强制线程从主内存重新加载所有共享变量(包括非 volatile 的普通变量),清空本地工作内存缓存; - 每次写入
state(如成功获取锁后设置setState(1)或释放锁时setState(0))都会触发“写 volatile 变量”动作,将当前线程工作内存中所有已修改的共享变量(不只是state)刷新回主内存; - 这并非仅同步
state本身,而是借助 volatile 的语义“捎带”完成临界区内所有共享变量的可见性保障。
happens-before 关系确保跨线程可见
根据 JSR-133 内存模型,unlock 操作 happens-before 后续的 lock 操作(针对同一锁实例)。具体表现为:
- 线程 A 执行
unlock()时,对state的写操作(如设为 0)构成一个 volatile 写; - 线程 B 随后执行
lock(),其对state的读操作(如读到 0 后 CAS 成功)构成一个 volatile 读; - JMM 规定:前一个 volatile 写与后一个 volatile 读之间建立 happens-before 关系,从而保证线程 A 在 unlock 前对任意共享变量(如
count、flag等非 volatile 字段)的所有写操作,对线程 B 在 lock 后的读操作全部可见。
底层由内存屏障与 LOCK 指令支撑
volatile 的语义在硬件层面由 JVM 实现保障:
- 对
state的写操作前插入StoreStore和StoreLoad内存屏障,禁止编译器和 CPU 将临界区内的写操作重排序到 unlock 之后; - 对应汇编指令通常包含
lock addl $0x0, (%esp)(或类似带LOCK前缀的指令),该指令强制将当前 CPU 写缓冲区中的所有待刷新数据(不限于state)一次性写回主内存,并使其他 CPU 缓存行失效; - 因此,unlock 不是“只刷
state”,而是把整个临界区结束时工作内存里所有脏数据一并同步出去。
与 synchronized 语义一致,无需额外 volatile 修饰
Java 规范明确要求:所有 Lock 实现必须提供与内置 monitor 锁相同的内存同步语义。也就是说,ReentrantLock 的 lock/unlock 对等价于 synchronized 的 monitorenter/monitorexit:
- 临界区内的变量读写天然具备原子性 + 可见性;
- 被保护的共享变量(如
int count)无需再声明为volatile; - 若在临界区外单独读写某个标志位(如轮询用的
boolean running),仍需volatile或其他同步手段,因为那已超出锁的保护范围。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











