jmm通过synchronized、lock、原子类保障原子性,volatile、synchronized、final保障可见性,happens-before规则、volatile、synchronized保障有序性。

Java 内存模型(JMM)通过一套抽象规则和具体机制,分别对原子性、可见性、有序性提供保障。这三者不是孤立存在的,而是共同支撑线程安全的基础。
原子性怎么保证
原子性指操作不可分割——要么全做,要么不做。JMM 本身只对极少数底层操作默认保证原子性,其余需开发者主动干预。
-
天然原子的操作:基本类型(
int、boolean、float、char等)的单次读或写;引用类型的赋值(即对象地址的读写);long和double在 64 位 JVM 中通常也是原子的(JDK 5+ 默认保证) -
非原子的典型操作:
i++、i += 1、new Object()等,实际包含“读-改-写”多步,中间可能被其他线程打断 -
保障手段:
-
synchronized块/方法:利用 monitor 锁确保临界区串行执行 -
java.util.concurrent.locks.Lock(如ReentrantLock):显式加锁解锁,控制访问粒度 - 原子类(
AtomicInteger、AtomicReference等):基于 CAS(Compare-and-Swap)指令实现无锁原子更新
-
可见性怎么保证
可见性解决的是“一个线程改了,另一个线程能不能马上看到”的问题。根源在于每个线程有自己工作内存,变量副本未必及时同步到主内存。
- volatile 关键字:写操作后强制刷新到主内存,读操作前强制从主内存重载——不加锁也能让修改对其他线程立即可见
- synchronized / Lock:退出同步块时,会将工作内存中所有共享变量刷新回主内存;进入时,会清空本地副本并重新从主内存加载
- final 字段:构造器中一旦初始化完成,其值对其他线程可见(配合 happens-before 规则)
-
线程启动与终止:调用
start()前的写操作对新线程可见;线程结束前的写操作对join()的线程可见
有序性怎么保证
有序性指代码执行顺序与预期一致。JVM 和 CPU 为优化性能会重排指令,但 JMM 定义了哪些重排是允许的、哪些必须禁止。
-
happens-before 原则:这是 JMM 定义的最核心的内存可见性与顺序约束规则。只要两个操作满足 happens-before 关系,JVM 就保证前者对后者可见且有序。常见规则包括:
- 程序顺序规则:同一个线程内,按代码顺序,前面的操作 happens-before 后面的操作
- 监视器锁规则:解锁操作 happens-before 后续对该锁的加锁操作
- volatile 规则:对 volatile 变量的写操作 happens-before 后续对该变量的读操作
- 线程启动规则:
Thread.start()happens-before 子线程任意动作 - 线程终止规则:子线程所有操作 happens-before 主线程检测到该线程结束(如
join()返回)
- volatile:不仅保证可见性,还插入内存屏障(Memory Barrier),禁止特定方向的指令重排序(如写 volatile 变量前后的指令不能重排到其后)
- synchronized / Lock:加锁与解锁天然构成 happens-before 边界,间接约束了临界区内外的执行顺序
三大特性相互交织:比如 synchronized 既锁住执行路径(保原子性),又刷新内存(保可见性),还建立 happens-before(保有序性)。理解它们各自的边界与协同方式,才能写出真正可靠的并发代码。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











