jmm的根本问题是工作内存与主内存分离导致可见性、原子性、有序性三大并发问题:线程操作变量副本而非主内存,引发同步延迟、操作拆分和指令重排。

Java内存模型(JMM)中,多线程对共享变量的修改之所以容易出问题,根本原因在于每个线程操作的是自己工作内存里的变量副本,而不是直接读写主内存。这种设计兼顾了性能(避免频繁访问慢速主存),但也引入了可见性、原子性和有序性三大问题。
工作内存与主内存的分离机制
主内存是所有线程共享的区域,存放实例字段、静态字段、数组元素等共享变量;而每个线程拥有私有的工作内存,它保存着该线程用到的变量在主内存中的副本。线程所有读写操作都必须发生在工作内存中——不能跳过工作内存直接操作主内存。
这种抽象对应硬件现实:工作内存相当于CPU寄存器+高速缓存,主内存则类比物理内存。当线程A修改一个int型共享变量时,实际流程是:
- 先从主内存read该变量值
- 再load进自己的工作内存
- 在工作内存中assign新值(如count++)
- 最后store回主内存,并write生效
交互操作的8种原子动作及其约束
JMM定义了lock、unlock、read、load、use、assign、store、write共8种不可分割的底层操作,它们共同构成变量同步的骨架。关键规则包括:
- read和load必须成对出现,且顺序固定:必须先read再load
- store和write也必须成对,且store发生在write之前
- assign后必须发生store/write,禁止“只改不回”
- 不允许无assign就store/write(防止脏写)
- 同一变量不能连续lock,必须unlock后才能重入
这些规则不是凭空设定,而是为了在不同CPU架构(x86/ARM)和编译器优化下,仍能保证语义一致。
为什么修改对其他线程“看不见”?
可见性问题的本质,就是工作内存与主内存之间的同步延迟。例如:
- 线程A将变量x从1改为2,完成assign但尚未store/write
- 此时线程B从主内存read x,拿到的仍是旧值1
- 即使A后续完成了store/write,B若未重新read/load,仍会继续使用自己工作内存里缓存的1
这种现象不是bug,而是JMM默认行为——它不强制实时同步,除非你显式干预。
让修改对其他线程立即可见的手段
要打破默认的延迟同步,需借助JMM提供的同步机制:
- volatile:对变量的每次read都强制从主内存加载,每次write都立即刷回主内存;还禁止相关指令重排序
- synchronized:进入时清空工作内存(强制read/load最新值),退出时将所有assign过的变量store/write回主内存
- final字段:构造器内初始化完成后,对该final变量的读取具有初始化安全性保障
- java.util.concurrent.atomic:如AtomicInteger,内部通过CAS+volatile组合实现原子更新与可见性
这些机制不是绕过JMM,而是利用JMM定义的规则,在特定节点插入必要的内存屏障(Memory Barrier),控制read/load/store/write的时机和顺序。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











