java内存模型(jmm)的核心作用是定义线程如何读写共享变量及操作何时对其他线程可见;它通过happens-before规则保障可见性、有序性与原子性,避免因数据争用(data race)导致的值丢失、结果不一致等无序行为。

Java内存模型(JMM)的核心作用,是定义线程如何读写共享变量、以及这些操作在何时对其他线程可见。当代码没有正确同步时,Data Race(数据争用)就会出现,进而导致看似“无序”的行为——比如变量值突然丢失、结果不一致、甚至程序逻辑彻底错乱。这不是bug,而是JMM允许的合法执行结果。
什么是Data Race
满足以下三个条件,就构成一次Data Race:
- 一个线程对某个变量执行写操作(write)
- 另一个线程对同一变量执行读操作(read)
- 这两个操作之间**没有happens-before关系**(即未通过synchronized、volatile、锁、线程启动/终止等规则建立顺序约束)
一旦发生Data Race,JMM不再保证程序的执行结果与顺序一致性模型一致,编译器、JVM、CPU都可能自由重排序、缓存延迟刷新、甚至返回过期值。
为什么会出现“无序”现象
这种无序不是随机,而是源于三层脱节:
- 编译器重排序:为优化性能,把a=1; b=2;变成b=2; a=1; ——单线程结果不变,但多线程下另一线程可能看到b已更新而a仍是旧值
- 处理器乱序执行:CPU可能先执行store指令(写入缓存),再执行前面的load(读取),且不同核心缓存未及时同步
- 工作内存与主内存不同步:线程A把i=1写入自己工作内存后未刷回主存;线程B从主存读i,仍得到0
这三者叠加,就让两个线程看到的变量状态完全不一致,表现为“值跳变”“计数不准”“if判断失效”等典型症状。
一个经典例子说明问题
假设有两个线程同时执行i++(初始i=0):
- 线程A读i=0 → 计算0+1=1 → 写回i=1(但尚未刷到主存)
- 线程B也读i=0(因A的更新还没可见)→ 计算0+1=1 → 写回i=1
- 最终i=1,而不是预期的2
这不是竞态“偶尔出错”,而是JMM在无同步前提下明确允许的结果——因为两次读、两次写之间没有任何happens-before约束,它们可被任意交错、重排、延迟。
怎样避免Data Race带来的无序
关键不是“加锁越早越好”,而是建立明确的happens-before链:
- 用synchronized块或方法:进入锁建立hb,退出锁也建立hb,确保临界区内所有读写对后续获取该锁的线程可见
- 声明变量为volatile:每次读都从主存加载,每次写都立即刷回主存,并禁止前后指令重排序
- 使用final字段:对象构造完成时,final域的值对其他线程立即可见(基于初始化安全保证)
- 优先选用java.util.concurrent包中的线程安全类型(如AtomicInteger、ConcurrentHashMap),它们内部已封装了正确的内存语义
只要所有共享变量的读写都被纳入某个happens-before关系中,JMM就保证程序具有顺序一致性——即执行效果等价于所有操作按某种全局顺序串行执行。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











