Java 内存模型(Java Memory Model,简称 JMM)是并发编程中至关重要的一部分,它定义了在多线程环境下变量的可见性有序性原子性等关键行为。下面我们从基础到进阶,带你理解 JMM 的核心机制。


一、什么是 Java 内存模型?

JMM 是 Java 虚拟机(JVM)中关于多线程共享内存访问的规范,它描述了:

  • 各线程如何将变量从主内存拷贝到工作内存
  • 变量在何时对其他线程可见
  • 编译器与 CPU 可进行哪些指令重排序优化

JMM 并不是真实存在的一块内存,而是一种抽象规范。


二、JMM 的关键概念

1. 主内存与工作内存

  • 主内存(Main Memory):所有线程共享的区域,保存所有变量的真实值。
  • 工作内存(Working Memory):每个线程私有,保存变量的副本。

线程操作变量流程如下:

1
主内存 ←→ 工作内存 ←→ 执行引擎

2. 可见性

当一个线程修改了变量,必须刷新回主内存,否则其他线程看不到这个修改。
使用 volatile 可以保证可见性。

3. 有序性

编译器和处理器会对代码进行指令重排序,但 JMM 保证:

  • 单线程内语义不变
  • volatilesynchronizedfinal 可以部分禁止重排序

4. 原子性

基本数据类型的读写是原子性的,除非是 longdouble(Java 5 之前)。
原子类如 AtomicInteger 则通过 CAS 实现更高层次的原子性。


三、JMM 中的 happens-before 规则

happens-before 是 JMM 保证线程安全的核心规则。如果一个操作 A happens-before B,那么 A 的结果对 B 可见,且 A 的执行在 B 之前。

常见的规则包括:

  1. 程序顺序规则:单线程中操作按代码顺序执行。
  2. 监视器锁规则:解锁 unlock 先于加锁 lock
  3. volatile 变量规则:写 volatile 先于读 volatile
  4. 线程启动规则:Thread.start() 先于新线程所有操作。
  5. 线程终止规则:线程所有操作先于 Thread.join() 返回。
  6. 传递性:A happens-before B,B happens-before C ⇒ A happens-before C。

四、关键字与内存模型的关系

1. volatile

  • 禁止指令重排序
  • 保证变量对所有线程可见
  • 不保证原子性

2. synchronized

  • 保证可见性和原子性
  • 同步代码块有入口和退出的内存屏障,线程进入临界区时必须读取主内存,退出时必须写回主内存

3. final

  • 保证被赋值后对象引用不可更改
  • 在构造函数中正确设置 final 字段能确保构造完成后对其他线程可见

五、典型示例分析

错误示例(未加 volatile):

1
2
3
4
5
6
7
8
9
10
11
12
13
class Example {
boolean flag = false;

public void run() {
while (!flag) {
// do something
}
}

public void stop() {
flag = true;
}
}

可能出现的问题:主线程修改了 flag,子线程由于缓存没有同步,导致死循环。

正确写法:

1
volatile boolean flag = false;

六、总结

特性 普通变量 volatile synchronized
原子性
可见性
有序性控制 ✅(部分)

Java 内存模型看似抽象,但在并发编程中起着决定性的作用。如果你在多线程中遇到“看不懂的行为”,十有八九和 JMM 脱不开关系。


以上内容如果有理解不清的地方,欢迎大家留言交流、批评指正~ 咱们继续修炼 Java 的内功心法 🔥!