jmm保证读取未同步变量绝不会返回“凭空捏造”的非法值,即所有读取结果必为该变量生命周期中真实存在过的合法值(如默认值、某线程某次写入值),通过类型安全初始化、基础原子性保障及禁止非法重排序实现;但这不等于可见性或有序性保障。

Java内存模型(JMM)对“读取未同步变量时不会读到凭空捏造的值”这一保证,本质上是**防止出现语义上完全非法的数据**,比如一个 int 变量读出 0xCAFEBABE 这类既非初始值、也非任何线程写入过的值。这不是说读取一定可见、一定最新,而是说它绝不会“无中生有”。
什么叫“凭空捏造的值”?
在缺乏内存模型约束的底层硬件或编译器行为下,可能出现以下非法情况:
- 读取一个未初始化的
long或double(64位),却得到高位和低位来自不同写操作的“拼接值”,比如高位是线程A写入的 0x1111,低位是线程B写入的 0x2222,组合成一个从未被任何线程完整写过的 0x11112222; - 读取一个
boolean字段,返回既不是true也不是false的某个比特模式(如 0xFF); - 读取一个对象引用,得到一个根本不能解引用的“野指针”地址(如 0xdeadbeef),导致 JVM 崩溃或不可预测行为。
JMM 明确禁止这类结果——它要求所有读操作返回的值,必须是该变量**生命周期中某个时刻真实存在过的值**(包括默认初始化值、某个线程某次写入的值),即所谓“合法值保障”(well-formed value guarantee)。
这个保证是怎么实现的?
它不依赖 synchronized 或 volatile,而是 JMM 的底层基石性约束,主要通过三方面落实:
-
类型安全与默认初始化:Java 所有字段在创建时(如对象实例化、类加载)都会被赋予确定的默认值(
0、false、null),不存在“未定义内存”; -
原子性基础保障:对 32 位及以下的基本类型(
int、char、float、引用等)的读写是原子的;对long和double,JVM 实现必须保证其读写要么是原子的,要么至少保证“不会撕裂为两个无关的32位值”(即禁止跨写拼接); - 禁止非法重排序:编译器和处理器不得将变量的读操作重排到其所在对象构造完成之前(否则可能读到部分构造的非法状态),也不得让读操作“看到”尚未发生的写操作所生成的任意比特组合。
它和“可见性”不是一回事
注意:这个保证不等于可见性。例如:
static int x = 0; // 线程A: x = 42; // 线程B(无同步): int r = x; // r 可能是 0,也可能是 42,但绝不可能是 999 或 0xABCDEF00
这里 r 读到 0 是合法的(因为 x=42 的写入对线程B不可见),但读到 999 就违反了 JMM——因为没有任何线程写过 999,也没有默认值是它,它就是“凭空捏造”的。
实际开发中要注意什么?
虽然有这层兜底保障,但绝不应依赖它来编写正确并发代码:
- 它只防“非法”,不保“正确”——读到旧值仍可能导致逻辑错误(如双重检查锁中未加
volatile导致看到部分构造对象); - 它不解决有序性问题,重排序仍可能导致观察到不合逻辑的执行顺序;
- 真正需要线程间通信时,必须使用
volatile、synchronized、final或java.util.concurrent工具来建立 happens-before 关系。
简言之,JMM 的“不凭空捏造”是安全底线,不是功能承诺。守住它,程序不会崩溃;但要让它行为可预期,还得靠显式同步。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











