Java 内存模型(JMM)全景图:并发世界的底层基石

摘要

Java 内存模型(JMM)是理解并发编程的核心基础。它定义了线程之间如何通过内存交互、JVM 如何处理指令重排、编译器和 CPU 如何影响执行结果。本文将以全景图的方式,系统梳理 JMM 的关键概念、运行机制和实践场景,帮助读者从根本上理解 Java 并发的底层逻辑。


一、为什么需要 JMM?

在单线程环境中,程序员可以"顺序执行思维"来推导结果。但在多线程环境下,情况会复杂得多:

  • CPU 优化:现代处理器会乱序执行指令,以提高吞吐量。
  • 编译器优化:Java 编译器可能会对字节码进行重排序,保持语义等价但执行顺序改变。
  • 缓存机制:线程可能并不直接读写主内存,而是读写各自 CPU 缓存中的副本。

如果没有一套严格的规则,开发者将无法预测多线程程序的执行结果。这套规则就是 Java 内存模型(JMM)


二、JMM 的核心目标

JMM 要解决的两个关键问题是:

  1. 可见性:一个线程修改的变量值,什么时候能被其他线程看到?
  2. 有序性:在多线程环境下,哪些代码执行顺序必须保持不变,哪些可以被编译器/CPU 调整?
  3. 原子性:某些操作是否能保证不可分割?

简单说,JMM 是 JVM 与开发者之间的"契约",它保证在特定规则下,程序的并发执行结果是可预测的。


三、JMM 的抽象全景图

我们可以用一张 抽象图 来理解 JMM 的核心概念:

  • 主内存:所有共享变量都存放在这里。
  • 工作内存:每个线程都有独立的工作内存,存放主内存变量的副本。
  • 交互方式:线程之间不直接通信,而是通过主内存实现数据交互。

四、JMM 的 8 种内存交互操作

Java 内存模型定义了 8 种操作,来规范线程与主内存之间的交互:

  1. lock(锁定) :作用于主内存变量,标识该变量为线程独占。
  2. unlock(解锁) :释放变量,使其能被其他线程访问。
  3. read(读取) :从主内存读取变量值到线程工作内存。
  4. load(载入) :将 read 得到的值放入工作内存变量副本。
  5. use(使用) :将工作内存中的值传递给执行引擎。
  6. assign(赋值) :把执行引擎计算的值写入工作内存变量。
  7. store(存储) :把工作内存变量的值写入主内存。
  8. write(写入) :将 store 的值最终写入主内存变量。

要点 :这 8 种操作必须成对出现,比如 readload 搭配,storewrite 搭配。


五、JMM 与三大特性

JMM 直接对应并发编程的三大特性:

1. 原子性

  • JMM 保证 基本数据类型的读写操作是原子性的(long 和 double 在 32 位 JVM 上例外)。
  • 对于复合操作(如 i++),需要通过 synchronizedAtomicInteger 来保证原子性。

2. 可见性

  • JMM 规定,线程对变量的修改,必须同步回主内存,其他线程才能看到。
  • volatile 关键字就是可见性保证的一种手段。

3. 有序性

  • 在单线程中,代码顺序看似固定,但编译器和 CPU 可能会重排指令。
  • JMM 通过 happens-before 原则 来保证关键有序性。

六、happens-before 原则

JMM 并不禁止所有的重排序,而是通过 happens-before 定义了必要的顺序关系:

  1. 程序次序规则:一个线程内,按照代码顺序执行。
  2. 锁规则:unlock 必然发生在后续 lock 之前。
  3. volatile 规则:对一个 volatile 变量的写,先行发生于后续对该变量的读。
  4. 传递性:A happens-before B,B happens-before C,则 A happens-before C。
  5. 线程启动规则:Thread.start() happens-before 线程 run()。
  6. 线程终止规则:线程的所有操作 happens-before 其他线程检测到它终止。

这套规则为我们推导并发结果提供了理论工具。


七、关键关键字与 JMM 的关系

1. volatile

  • 保证变量的 可见性有序性(禁止指令重排)。
  • 不保证操作的 原子性

2. synchronized

  • 保证 原子性(临界区互斥)。
  • 保证 可见性(进入 synchronized 时,工作内存会清空,从主内存读取最新值)。
  • 保证 有序性(临界区内的代码不会被重排到外部)。

3. final

  • 保证初始化安全性:一旦对象构造完成,final 字段对所有线程可见。

八、典型案例解析

案例 1:双重检查锁(DCL)单例

错误实现(缺少 volatile):

java 复制代码
public class Singleton {
    private static Singleton instance; // 没有volatile
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

问题:对象初始化过程可能被指令重排,导致其他线程读到"未完全初始化"的对象。

正确实现:

java 复制代码
public class Singleton {
    private static volatile Singleton instance;
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

案例 2:volatile 的可见性测试

java 复制代码
class TestVolatile {
    private static boolean running = true;
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            while (running) { }
        }).start();

        Thread.sleep(1000);
        running = false; // 线程可能看不到变化
    }
}

解决方案:running 使用 volatile 修饰。


九、实践建议

  1. 避免过度依赖 JMM :尽量使用 java.util.concurrent 包提供的并发工具(如 AtomicXXXLockExecutor)。
  2. 理解 volatile 的边界:适合状态标记,不适合复杂复合操作。
  3. 锁是大杀器synchronized 在 JDK 1.6 之后优化明显,性能远超过去印象。
  4. 多线程调试 :使用 jconsolejstack 分析线程状态,辅助验证并发逻辑。

十、总结

Java 内存模型(JMM)是并发编程的底层基石。

  • 它定义了主内存与工作内存的交互规则;
  • 通过 happens-before 保证了关键的有序性;
  • 借助 volatilesynchronizedfinal 等关键字,开发者可以在复杂的多线程环境下写出正确的代码。
相关推荐
從南走到北6 分钟前
JAVA国际版东郊到家同城按摩服务美容美发私教到店服务系统源码支持Android+IOS+H5
android·java·开发语言·ios·微信·微信小程序·小程序
毅航1 小时前
从原理到实践,讲透 MyBatis 内部池化思想的核心逻辑
后端·面试·mybatis
qianmoq1 小时前
第04章:数字流专题:IntStream让数学计算更简单
java
展信佳_daydayup1 小时前
02 基础篇-OpenHarmony 的编译工具
后端·面试·编译器
Always_Passion1 小时前
二、开发一个简单的MCP Server
后端
用户721522078771 小时前
基于LD_PRELOAD的命令行参数安全混淆技术
后端
笃行3501 小时前
开源大模型实战:GPT-OSS本地部署与全面测评
后端
知其然亦知其所以然1 小时前
SpringAI:Mistral AI 聊天?一文带你跑通!
后端·spring·openai
庚云1 小时前
🔒 前后端 AES 加密解密实战(Vue3 + Node.js)
前端·后端
带只拖鞋去流浪1 小时前
Java集合(Collection、Map、转换)
java