深入理解 Java 内存模型(JMM)及其在并发编程中的应用

深入理解 Java 内存模型(JMM)及其在并发编程中的应用

一、前言

在 Java 并发编程中,线程安全一直是开发者关注的重点。而 Java 内存模型(Java Memory Model, JMM)正是理解线程之间通信与同步的基础。今天我们就从 JMM 的概念、可见性、原子性、有序性出发,带你深入剖析 JMM 的核心原理。


二、什么是 Java 内存模型?

JMM 并不是一个真实存在的内存结构,而是一组规范,定义了:

  • 线程之间如何通过主内存共享变量
  • 每个线程的本地工作内存如何与主内存进行交互

JMM 内存结构示意图:

                ┌─────────────┐
                │   主内存    │
                │(共享变量区) │
                └─────┬───────┘
                      │
     ┌────────┐   ┌───▼─────┐   ┌────────┐
     │线程1    │   │线程2    │   │线程3    │
     │工作内存 │   │工作内存 │   │工作内存 │
     └────────┘   └────────┘   └────────┘

主内存保存所有共享变量,而每个线程都有自己的工作内存,保存了主内存中变量的副本。线程只能操作自己工作内存中的变量,若要相互通信,必须通过主内存。


三、JMM 的三大特性

1. 可见性(Visibility)

当一个线程修改了共享变量的值,其他线程能立刻看到最新值,称为可见性

解决方案:

  • 使用 volatile 修饰变量
  • 使用 synchronizedLock 同步机制

2. 原子性(Atomicity)

操作要么全部完成,要么全部不做,中间不允许中断。

示例:i++ 不是原子操作,它包含:读取 -> 修改 -> 写入

解决方案:

  • 使用原子类,如 AtomicInteger
  • 使用 synchronized 块保护临界区

3. 有序性(Ordering)

JVM 和 CPU 会进行指令重排序以优化性能,但这可能打破逻辑顺序。

解决方案:

  • 使用 volatile(禁止重排序)
  • 使用内存屏障(编译器级控制)

四、volatile 是什么?怎么保证可见性与有序性?

volatile 关键字有两个核心作用:

  1. 保证可见性:修改的值会立即刷新到主内存,其他线程读取到的是最新值。
  2. 禁止指令重排序:防止重排序破坏语义。

示例:

public class VolatileDemo {
    private volatile boolean flag = false;

    public void start() {
        new Thread(() -> {
            while (!flag) {
                // do something
            }
        }).start();

        new Thread(() -> {
            flag = true;
        }).start();
    }
}

如果没有 volatile,子线程可能永远感知不到 flag = true 的变化。


五、JMM 与 synchronized 的关系

synchronized 是 JMM 实现可见性与原子性的重要手段。

  • 进入 synchronized 块:工作内存会清空副本,从主内存读取变量
  • 离开 synchronized 块:将工作内存的变量刷新到主内存

这使得同步块之间的数据始终保持一致。


六、volatile 和 synchronized 的区别总结

特性volatilesynchronized
原子性✘ 不保证✔ 保证
可见性✔ 保证✔ 保证
有序性✔ 禁止指令重排✔ 隐式禁止(通过内存屏障)
性能开销较小较大
适用场景状态标志、双重检查锁等复合操作、临界区访问等

七、JMM 在实际中的应用场景

  1. 单例模式中的双重检查锁
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;
    }
}
  1. 自旋等待状态变量变更

八、总结

Java 内存模型(JMM)并不是纸上谈兵,它影响了并发程序中变量共享、线程通信、同步行为的方方面面。掌握 JMM 是理解 Java 并发程序正确性和性能优化的基础。

Comments

No comments yet. Why don’t you start the discussion?

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注