读屏障(LoadLoad/LoadStore)和写屏障(StoreStore/StoreLoad)是JVM自动插入的硬件级指令,通过lfence/dmb或lock/stlr等原语约束CPU流水线与缓存行为,确保volatile读写前后操作的有序性与可见性,但不保证原子性。

读屏障和写屏障在底层不是 Java 代码里的语法,而是 JVM 根据语义自动插入的硬件级指令约束,它们通过干预 CPU 的执行流水线和缓存行为来保证有序性。
读屏障如何在硬件层面锁住读顺序
读屏障(如 LoadLoad 和 LoadStore)本质是告诉 CPU:“这一行读操作必须完成,后面的读/写才能开始”。它不阻止所有重排,只封堵特定组合:
- LoadLoad 屏障:确保屏障前的读操作一定在屏障后的读操作之前完成——比如 if (flag) 是 volatile 读,JVM 会在它前面插 LoadLoad,防止编译器或 CPU 把 int x = data 提前到 flag 判断之前
- LoadStore 屏障:确保该读之后的写不能被提前——比如 while (!done) { count++; } 中 done 是 volatile,JVM 插入 LoadStore,避免 JIT 把 count++ 移到 while 条件判断前执行
- x86 上常用 lfence 指令实现;ARM 等弱内存序平台则用 dmb ishld(data memory barrier, inner shareable load-dependant),强制按序加载并同步到共享域
写屏障如何在硬件层面锁住写顺序
写屏障(如 StoreStore 和 StoreLoad)核心作用是“把写钉住”,让其成为内存操作的时间锚点:
- StoreStore 屏障:确保屏障前的普通写(如 value = 42)一定先于 volatile 写(如 ready = true)提交到本地 cache——x86 天然支持 store FIFO,常省略显式 sfence;ARM 必须用 dmb ishst 刷写 buffer 并阻塞后续写入
- StoreLoad 屏障:最重的一类,要求 volatile 写之后的所有读,都必须等该写真正刷新到主存、且其他核完成失效响应后才可执行——x86 用带 lock 前缀的指令(如 lock addl $0, (%rsp))触发总线锁定或缓存行独占,强制 MESI 协议广播 invalidate 消息
- 它不只是“等写完”,而是协调 store buffer、invalidation queue 和 cache coherency 状态,达成跨核可见性与顺序一致性
屏障不是插入一行汇编,而是贯穿整个执行链路
JVM 不直接生成裸指令,而是通过多层协同让屏障生效:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 编译器(javac / JIT):生成字节码时避开跨屏障移动;逃逸分析遇到 volatile 字段会禁用字段冗余消除
- JIT 运行时:根据目标 CPU 架构选择对应原语——x86 用 lock 指令模拟 full barrier,AArch64 用 stlr(store-release)+ ldar(load-acquire)对
- CPU 硬件:真正执行时,靠内存屏障指令暂停流水线、清空 store buffer、等待缓存一致性协议确认,最终使“程序看到的顺序”与“硬件执行的顺序”对齐
屏障只管相关指令,不管无关逻辑
内存屏障的作用范围非常明确:
- 它只约束与 volatile 变量直接关联的那一次读或写,以及它前后紧邻的普通内存操作
- 不会禁止两个完全无关变量之间的重排,比如 a = 1; b = 2; 即使都是普通变量,也可能被重排,因为没屏障介入
- 也不会包裹复合操作——counter++ 的读-改-写三步之间没有屏障保护,所以仍可能被其他线程打断
不复杂但容易忽略:屏障不是魔法开关,它是 JVM 和硬件共同遵守的一套契约,靠精确控制指令边界来守住多线程下最基础的执行秩序。

















