什么是JMM?

By | 2025-04-28

JMM(Java Memory Model,Java内存模型)是 Java 编程语言中的一种规范,它定义了 Java 程序中各个线程如何通过共享变量进行通信以及如何在并发环境中保证数据的可见性和一致性。JMM 是 Java 在多线程环境中确保正确执行的一项关键机制,目的是为了解决在多核处理器上执行时,如何管理和同步不同线程对共享变量的访问问题。

JMM 主要解决以下几个方面的问题:

  1. 可见性(Visibility)

在多线程环境中,一个线程对共享变量的修改,其他线程能否看到这个修改,依赖于 可见性。JMM 规定了共享变量在不同线程之间的更新规则,确保一个线程对共享变量的修改可以及时传播给其他线程。

例如,如果线程 A 修改了一个变量的值,线程 B 能否立即看到这个修改,取决于 JMM 中的内存同步规则。

  1. 有序性(Ordering)

在多核处理器中,编译器和 CPU 可能会对指令进行重排序优化,导致程序的执行顺序与代码中写出的顺序不同。JMM 通过规则来控制这种重排序,确保多线程程序中的执行顺序符合预期。

例如,JMM 定义了 happens-before 规则,它是保证操作顺序的一种方式。通过 synchronized、volatile 等关键字,JMM 保证了在某些操作之间的顺序性。

  1. 原子性(Atomicity)

JMM 本身并不直接保证原子性,原子性是指某个操作在执行时不能被中断,JMM 通过特定的同步机制来保证原子性。在 Java 中,synchronized 和 java.util.concurrent 包中的类(如 ReentrantLock)可以用于保证操作的原子性。

JMM 相关的关键概念

  1. 主内存和工作内存

JMM 定义了两种内存区域:
• 主内存(Main Memory):所有线程共享的内存区域,所有的变量(字段)都存储在主内存中。
• 工作内存(Working Memory):每个线程私有的内存区域,线程中的所有变量的副本都保存在工作内存中。线程操作的变量实际上是在工作内存中进行的,而不是直接访问主内存。

线程对变量的所有读写操作,都会首先在其工作内存中进行,而不是直接操作主内存中的数据。工作内存中的变量副本是线程私有的,其他线程看不到。

  1. happens-before 规则

为了保证线程间的正确同步,JMM 定义了 happens-before 规则。这些规则确保了在并发执行的程序中,某些操作的执行顺序是被正确约束的。

常见的 happens-before 规则有:
• 程序顺序规则:在同一个线程内,按程序顺序执行的操作,前面的操作 happens-before 后面的操作。
• 监视器锁规则:对一个锁的解锁操作 happens-before 该锁的加锁操作。
• volatile 变量规则:对一个 volatile 变量的写操作 happens-before 任何后续线程对该变量的读操作。
• 线程启动规则:在主线程调用 Thread.start() 启动线程后,主线程对该线程的操作 happens-before 新线程的执行开始。

  1. synchronized 和 volatile

在 JMM 中,synchronized 和 volatile 是两个非常重要的关键字,它们与内存的可见性和顺序性紧密相关。
• synchronized:当一个线程执行 synchronized 代码块时,它会先获得锁,并在执行完后释放锁。JMM 保证了获取锁和释放锁时的内存可见性,确保了锁内的代码在不同线程之间有正确的同步。
• volatile:当一个变量被声明为 volatile 时,它保证了对该变量的读写操作不会被 JVM 或 CPU 重排,也保证了线程间对该变量的修改是可见的。也就是说,当一个线程修改了 volatile 变量,其他线程可以立即看到修改的结果。

  1. 内存屏障(Memory Barrier)

内存屏障是一种硬件或编译器提供的机制,确保特定操作的执行顺序。例如,synchronized 和 volatile 关键字在 Java 中实际上会生成内存屏障,阻止 JVM 的指令重排序,从而实现正确的顺序性。

JMM 与 Java 多线程的关系

在 Java 的多线程编程中,JMM 是确保线程安全的核心机制。它通过控制线程如何访问共享变量来解决一些常见的并发问题,例如:
• 可见性问题:多个线程同时访问共享变量时,线程 A 修改了共享变量,但线程 B 可能没有看到最新的值。通过 volatile 关键字或者 synchronized 来解决这个问题。
• 指令重排序问题:为了提高性能,编译器和 CPU 会对代码中的指令进行重排序,可能会导致并发执行时的异常行为。JMM 使用内存屏障来确保合适的顺序。
• 原子性问题:多个线程同时对共享数据进行修改时,可能会出现数据竞争问题。JMM 使用 synchronized 或其他并发控制机制来保证原子性。

总结

JMM 是 Java 中多线程编程的基础,它规定了在多线程环境下如何保证数据的可见性、有序性和一致性。通过合理使用 synchronized、volatile 等关键字以及遵循 happens-before 规则,Java 能够在多核处理器和并发执行的环境中实现高效且安全的多线程编程。

理解 JMM 的原理和规则,是写出高效且正确的并发程序的关键。如果你深入研究并发编程,理解 JMM 将大大帮助你写出线程安全且高效的代码。