java内存模型通过happens-before规则保障可见性,volatile强制主存读写并插入内存屏障,synchronized依赖锁释放与获取的顺序,final字段在构造完成时保证一次性可见。

Java 内存模型(JMM)中,可见性不是靠“立刻同步”或“实时刷新”实现的,而是通过明确的内存操作约束规则来保障一个线程对共享变量的修改能被其他线程正确感知。核心在于:不保证“什么时候看到”,但保证“一旦看到,就一定看到全部正确的前序状态”。
volatile 关键字是最轻量、最常用的可见性保障手段
- 每次读 volatile 变量,JVM 强制从主内存加载最新值,不使用工作内存中的缓存副本;
- 每次写 volatile 变量,JVM 强制将新值立即刷回主内存,并使其他线程对应变量的缓存失效;
- 底层通过插入内存屏障(StoreLoad / LoadLoad 等)阻止指令重排序,确保写操作之前的副作用(如对其他普通变量的赋值)对后续读该 volatile 变量的线程可见。
例如:
private static int data = 0;
private static volatile boolean ready = false;
// 线程 A 执行
data = 42; // 普通写
ready = true; // volatile 写 → 插入 StoreStore + StoreLoad 屏障
// 线程 B 执行
if (ready) { // volatile 读 → 插入 LoadLoad + LoadStore 屏障
System.out.println(data); // 一定能打印 42,不会是 0 或未定义值
}
只要线程 B 读到 ready == true,就说明 data = 42 这个操作对其可见——这是 happens-before 关系的直接体现。
synchronized 块也天然提供可见性保障
- 进入 synchronized 块前,线程会清空工作内存中相关变量的缓存,强制从主内存重新读取;
- 退出 synchronized 块时,会把工作内存中所有修改强制刷回主内存;
- 其内存语义基于“解锁 happens-before 后续加锁”,即:线程 A 释放锁前的所有写操作,对之后获取同一把锁的线程 B 是可见的。
final 字段在构造完成时提供一次性可见性
- 对象构造过程中对 final 字段的赋值,会在构造方法结束前完成写入;
- 其他线程通过合法途径(如正确发布)获得该对象引用后,一定能看到 final 字段的正确值,无需额外同步。
不依赖同步机制时,可见性无法保证
- 普通变量(非 volatile、非 final、未在 synchronized 中)的读写,可能长期停留在工作内存中;
- JIT 编译器可能将循环中对 flag 的读取优化为常量判断(如
while(flag)→while(true)),导致线程永远卡住; - CPU 缓存一致性协议(如 MESI)不保证跨核间写操作的即时传播,必须由 JMM 规则驱动刷新行为。
可见性本质上是逻辑可见性,不是物理即时性。它靠 happens-before 链建立操作之间的偏序关系,再由 JVM 和硬件协同落实为内存屏障与缓存控制。用对 volatile、synchronized 或 final,才能让“我改了”真正变成“你看见了”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











