内存屏障是解决指令重排序与可见性问题的底层硬件/编译器指令,通过强制同步缓存、禁止非法重排来保障多线程下操作的顺序性和最新值可见性。

内存屏障不是抽象概念,它是解决指令重排与可见性问题的底层抓手。当你看到一个线程改了值、另一个线程却读不到时,问题往往不在代码逻辑,而在CPU缓存未同步、编译器提前优化,或指令执行顺序被悄悄打乱。内存屏障正是用来“钉住”关键操作,强制硬件和编译器按你期望的顺序执行、同步。
为什么普通变量会“看不见”更新?
Java中每个线程有自己的工作内存(对应CPU缓存),变量读写不直接操作主内存。比如一个布尔标志stopRequested = false:
- 主线程设为true,可能只写入本地L1缓存,没刷回主内存;
- 工作线程循环读该变量,始终从自己缓存取旧值,永远不退出;
- 这不是bug,是JMM默认行为——普通变量不保证跨线程可见。
volatile如何用内存屏障“锁住”顺序?
声明volatile boolean stopRequested后,JVM会在字节码层面插入内存屏障:
- 写volatile变量前:插入StoreStore屏障,确保前面所有写操作已完成;
- 写volatile变量后:插入StoreLoad屏障,强制将该变量写回主内存,并使其他CPU缓存行失效(MESI协议触发);
- 读volatile变量时:插入LoadLoad + LoadStore屏障,强制从主内存重新加载,跳过陈旧缓存。
这相当于给一次写+一次读“加了两道门禁”,既防止重排,又打通可见路径。
实战看DCL单例里的重排陷阱
双重检查锁定(DCL)若不用volatile,对象构造可能被重排序:
- 正常三步:①分配内存 → ②初始化对象 → ③赋值给静态引用;
- 重排后可能变成:①分配内存 → ③赋值 → ②初始化(中间态);
- 此时另一线程看到引用非null,直接返回未初始化对象,触发NPE或诡异状态。
加上volatile后,JVM在赋值语句后插入StoreLoad屏障,彻底禁止③跑到②之前,保证看到的对象一定是初始化完成的。
内存屏障不是万能的,它不保证原子性
屏障管的是“顺序”和“可见”,不是“一次做完”。例如:
- volatile int count能保证每次读到最新值;
- 但count++是读-改-写三步,屏障无法阻止两个线程同时读到相同值、各自+1再写回,结果只加了1次;
- 这种场景仍需synchronized或AtomicInteger。

















