Java 内存模型(Java Memory Model,JMM)是 Java 并发编程的基石。它定义了多线程环境下共享变量的访问规则,确保程序在不同平台和处理器架构上都能表现出一致的行为。理解 JMM 不仅是高级 Java 开发者的必备技能,更是排查并发问题的关键。
现代计算机采用多层次缓存架构来提升性能:
这种架构带来两个问题:
Java 的设计目标是"一次编写,到处运行"。JMM 屏蔽了底层硬件和操作系统差异,为开发者提供统一的内存语义保证。
JMM 规定所有变量都存储在主内存中,每个线程有自己的工作内存。
关键规则:
JMM 定义了 8 种原子操作:
| 操作 | 作用域 | 描述 |
|---|---|---|
lock | 主内存 | 标识变量为线程独占 |
unlock | 主内存 | 释放变量的锁定状态 |
read | 主内存 | 将变量值传输到工作内存 |
load | 工作内存 | 将 read 的值放入变量副本 |
use | 工作内存 | 将变量值传递给执行引擎 |
assign | 工作内存 | 将执行引擎值赋给变量 |
store | 工作内存 | 将变量值传输到主内存 |
write | 主内存 | 将 store 的值写入变量 |
happens-before 是 JMM 的核心关系,用于判断操作间的可见性和有序性。JMM 内置 8 条规则,包括程序次序、锁定、volatile、传递性、线程启动/终止、中断和对象终结规则。
volatile 提供两项核心保证:
// 1. 状态标志
private volatile boolean running = true;
// 2. 双重检查锁定(DCL)
private volatile static Singleton instance;
// 3. 无需加锁的读操作
private volatile int value;
count++ 实际上包含三个步骤:读取值、增加值、写回值。volatile 不能保证这三步的原子性。
构造函数完成前不要让 this 引用逸出,否则其他线程可能看到未初始化的值。
无锁 → 偏向锁 → 轻量级锁 → 重量级锁
public class SafeDCL {
private volatile static SafeDCL instance; // 必须用 volatile!
public static SafeDCL getInstance() {
if (instance == null) {
synchronized (SafeDCL.class) {
if (instance == null) {
instance = new SafeDCL();
}
}
}
return instance;
}
}
final 域天然线程安全running、initializedAtomicInteger、AtomicLongConcurrentHashMap 优于 HashtableJava 内存模型是理解并发编程的核心:
核心原则:先保证正确性,再考虑性能。使用 java.util.concurrent 包中的工具类通常比手动实现更可靠。