什么是 Java 的内存模型?

Java 内存模型(Java Memory Model, JMM)是 Java 虚拟机(JVM)规范中定义的一种抽象模型,用于描述多线程环境下,线程如何与内存交互,以及如何保证线程之间的可见性有序性原子性。JMM 是 Java 并发编程的基础,它定义了线程如何访问共享变量,以及如何通过同步机制(如 volatilesynchronized 等)来协调线程之间的操作。


Java 内存模型的核心概念

1. 主内存与工作内存
  • 主内存(Main Memory)
    • 主内存是所有线程共享的内存区域,存储了所有的变量(实例字段、静态字段等)。
  • 工作内存(Working Memory)
    • 每个线程都有自己的工作内存,工作内存是线程私有的,存储了线程对主内存中变量的副本。
    • 线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接操作主内存。
2. 内存间的交互操作

JMM 定义了以下 8 种操作来完成主内存与工作内存之间的交互:

  1. lock(锁定):作用于主内存的变量,标识一个变量为线程独占状态。
  2. unlock(解锁):作用于主内存的变量,释放一个被锁定的变量。
  3. read(读取):从主内存中读取变量的值到工作内存。
  4. load(载入):将 read 操作读取的值放入工作内存的变量副本中。
  5. use(使用):将工作内存中的变量值传递给执行引擎(如 CPU)。
  6. assign(赋值):将执行引擎计算的结果赋值给工作内存中的变量。
  7. store(存储):将工作内存中的变量值传送到主内存。
  8. write(写入):将 store 操作传送的值写入主内存的变量中。

这些操作需要满足一定的规则,例如:

  • read 和 load、store 和 write 必须成对出现。
  • 不允许一个线程丢弃最近的 assign 操作(即变量在工作内存中改变了之后必须同步到主内存)。
  • 不允许一个线程无原因地(没有发生过任何 assign 操作)把数据从工作内存同步回主内存。
3. 可见性(Visibility)
  • 可见性是指一个线程对共享变量的修改,能够及时被其他线程看到,它通过内存屏障(Memory Barrier)实现。
  • JMM 通过以下机制保证可见性:
    • volatile 关键字:确保变量的修改对所有线程立即可见。
    • synchronized 关键字:在释放锁之前,会将工作内存中的变量同步到主内存。
    • final 关键字:确保变量在构造完成后对其他线程可见。

内存屏障
写屏障(Store Barrier):确保当前线程的写操作(对主内存的写操作)在此屏障之前完成,(它会立刻把这个修改同步到主内存中)。
读屏障(Load Barrier):确保当前线程的读操作在此屏障之后完成。
在 volatile 变量上操作时,JVM 会插入 写屏障 和 读屏障,以确保变量在不同线程之间的可见性。这样,当一个线程更新了 volatile 变量,另一个线程立刻能看到更新。

4. 有序性(Ordering)
  • 有序性是指程序执行的顺序按照代码的先后顺序执行。
  • JMM 通过以下机制保证有序性:
    • volatile 关键字:禁止指令重排序,确保变量的读写操作按顺序执行。
    • synchronized 关键字:确保同一时刻只有一个线程执行同步代码块。
    • happens-before 规则:定义了一些操作之间的先后顺序,确保前一个操作的结果对后一个操作可见。
5. 原子性(Atomicity)
  • 原子性是指一个操作是不可分割的,要么全部执行成功,要么全部不执行。
  • JMM 通过以下机制保证原子性:
    • synchronized 关键字:确保同步代码块的原子性。
    • java.util.concurrent.atomic:提供了一些原子类(如 AtomicInteger),通过 CAS(Compare-And-Swap)操作保证原子性。

happens-before 规则

happens-before 是 JMM 的核心规则之一,用于定义操作之间的可见性和顺序。以下是常见的 happens-before 规则:

  1. 程序顺序规则:在一个线程中,前面的操作 happens-before 后面的操作。
  2. volatile 规则:对一个 volatile 变量的写操作 happens-before 后续对这个变量的读操作。
  3. 锁规则:解锁操作 happens-before 后续的加锁操作。
  4. 线程启动规则:线程的 start() 方法 happens-before 该线程的任何操作。
  5. 线程终止规则:线程的所有操作 happens-before 其他线程检测到该线程已经终止。
  6. 传递性规则:如果 A happens-before B,且 B happens-before C,那么 A happens-before C。

Java 内存模型的作用

  1. 屏蔽硬件差异
    • JMM 定义了统一的内存模型,屏蔽了不同硬件和操作系统在内存访问上的差异。
  2. 保证线程安全
    • 通过 volatilesynchronized 等机制,确保多线程环境下的可见性、有序性和原子性。
  3. 优化编译器指令重排序
    • JMM 允许编译器和处理器对指令进行重排序,但必须遵守 happens-before 规则,确保程序执行结果的正确性。

示例:volatile 关键字的作用

public class VisibilityExample {
    private volatile boolean flag = false;

    public void writer() {
        flag = true; // 写操作
    }

    public void reader() {
        while (!flag) { // 读操作
            // 等待
        }
        System.out.println("Flag is true");
    }
}
  • 如果没有 volatilereader() 方法可能永远看不到 flag 的变化。
  • 使用 volatile 后,flag 的修改对所有线程立即可见。

总结

Java 内存模型(JMM)是 Java 并发编程的核心,它定义了多线程环境下线程如何与内存交互,并通过 happens-before 规则、volatilesynchronized 等机制保证了可见性、有序性和原子性。理解 JMM 对于编写高效、线程安全的并发程序至关重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值