Java 内存模型(JMM)详解

玄武2026-03-05字数:667阅读:约 3 分钟java-basicsJava基础JMM并发

Java 内存模型(JMM)详解

1. 为什么需要 JMM?

在 CPU 多核架构下,每个核心有自己的缓存(L1/L2 Cache),多线程程序中不同线程对共享变量的修改可能无法即时可见,导致可见性问题。Java 内存模型(JMM)就是为了定义多线程环境下变量访问规则而设计的。

JMM 解决三大问题:可见性、原子性、有序性。


2. JMM 核心概念

2.1 主内存与工作内存

┌──────────────────────────────────────┐
│              主内存 (Main Memory)     │
│   共享变量 x = 0                      │
└──────┬───────────────────┬───────────┘
       │ read/write        │ read/write
┌──────▼──────┐     ┌──────▼──────┐
│ 线程 A       │     │ 线程 B       │
│ 工作内存x=1  │     │ 工作内存x=0  │
└─────────────┘     └─────────────┘
  • 主内存:所有线程共享,存放实例字段、静态字段等
  • 工作内存:每个线程私有,存放该线程使用的变量副本

2.2 happens-before 原则

JMM 通过 happens-before 规则来保证内存可见性,主要规则:

规则说明
程序顺序规则同一线程内,代码按书写顺序执行
volatile 规则volatile 写 happens-before 后续的 volatile 读
锁规则unlock happens-before 后续对同一锁的 lock
线程启动规则Thread.start() happens-before 线程内的操作
传递性A hb B,B hb C,则 A hb C

3. volatile 关键字

volatile 保证可见性禁止指令重排,但不保证原子性。

public class VolatileExample {
    // volatile 保证主内存更新对所有线程立即可见
    private volatile boolean running = true;

    public void stop() {
        running = false; // 立即刷到主内存
    }

    public void run() {
        while (running) { // 每次从主内存读取最新值
            doWork();
        }
    }
}

volatile 底层原理

volatile 写操作会在指令前后加入内存屏障(Memory Barrier)

StoreStore 屏障
volatile 写
StoreLoad 屏障  ← 最重要,防止 volatile 写与后续读重排

4. synchronized 与 JMM

synchronized 同时保证可见性、原子性、有序性

public class SyncExample {
    private int count = 0;

    // synchronized 保证同一时刻只有一个线程执行
    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count; // 解锁前刷新到主内存,加锁后从主内存读取
    }
}

5. 常见面试题

Q:double/long 类型操作是原子的吗?

A:JVM 规范允许将 64 位的 long/double 的读写操作分成两次 32 位操作,在 32 位 JVM 上不是原子的。实际上现代 64 位 JVM 已经保证了原子性,但建议加 volatile 修饰。

Q:volatile 能替代 synchronized 吗?

A:不能。volatile 不保证复合操作的原子性(如 i++),只能在"一写多读"场景下替代 synchronized。


💡 总结:JMM 通过 happens-before、volatile、synchronized 三套机制保证并发安全。理解 JMM 是掌握 Java 并发编程的基础。

💬 文章评论 (0)

支持 Markdown,请友善交流
正在加载评论...