JMM内存模型是什么

发布时间:2022-02-19 10:02:19 作者:iii
来源:亿速云 阅读:152
# JMM内存模型是什么

## 引言

在多线程编程的世界中,**Java内存模型(Java Memory Model, JMM)**是确保程序正确性的核心理论基础。随着多核处理器成为现代计算机的标配,理解JMM对于编写高效、线程安全的Java程序至关重要。本文将深入剖析JMM的定义、核心概念、实现原理以及实际应用场景,帮助开发者跨越"可见性"与"有序性"的认知鸿沟。

## 一、JMM的定义与背景

### 1.1 什么是内存模型

内存模型(Memory Model)是计算机科学中描述**多线程环境下内存访问行为**的规范。它定义了:
- 线程如何通过内存进行交互
- 内存操作(读/写)的可见性规则
- 指令重排序的约束条件

### 1.2 JMM的诞生背景

Java最初的内存模型(Java 1.0-1.4)存在严重缺陷:
- 无法有效禁止编译器和处理器的优化重排序
- final字段的线程安全性无法保证
- volatile的语义不够严格

2004年JSR-133(Java内存模型与线程规范)彻底重构了JMM,并在Java 5.0中正式发布。

### 1.3 JMM的核心目标

1. **平台无关性**:在不同硬件架构上保持一致的并发语义
2. **性能优化空间**:允许编译器和处理器进行合理的优化
3. **程序员友好**:提供清晰的可预测行为

## 二、JMM的核心概念

### 2.1 主内存与工作内存

JMM将内存抽象为两层结构:

| 内存类型   | 存储内容                     | 特性                     |
|------------|------------------------------|--------------------------|
| 主内存     | 实例字段、静态字段、数组元素 | 线程共享                 |
| 工作内存   | 方法参数、局部变量           | 线程私有,存在可见性问题 |

```java
// 示例:共享变量与局部变量
class Example {
    static int sharedVar;  // 主内存
    void method() {
        int localVar = 0;  // 工作内存
    }
}

2.2 内存间交互操作

JMM定义了8种原子性内存操作(以下均为JVM内部操作):

  1. lock(锁定):作用于主内存变量
  2. unlock(解锁):作用于主内存变量
  3. read(读取):从主内存传输到工作内存
  4. load(载入):将read得到的值放入工作内存副本
  5. use(使用):执行引擎使用变量值
  6. assign(赋值):将新值赋给工作内存变量
  7. store(存储):将工作内存值传输到主内存
  8. write(写入):将store得到的值放入主内存变量

2.3 happens-before原则

JMM最关键的排序规则,定义6种天然happens-before关系:

  1. 程序顺序规则:同一线程内的操作按程序顺序发生
  2. 监视器锁规则:unlock操作先于后续的lock操作
  3. volatile规则:volatile写先于后续的读
  4. 线程启动规则:Thread.start()先于线程内任何操作
  5. 线程终止规则:线程中所有操作先于线程终止检测
  6. 传递性:若A hb B,B hb C,则A hb C
// happens-before示例
class HBExample {
    int x = 0;
    volatile boolean v = false;
    
    void writer() {
        x = 42;     // 1
        v = true;   // 2  volatile写
    }
    
    void reader() {
        if (v) {    // 3  volatile读
            System.out.println(x); // 保证看到42
        }
    }
}

三、JMM的三大特性

3.1 原子性(Atomicity)

JMM保证以下操作的原子性: - 基本类型(除long/double)的读写 - volatile修饰的long/double的读写 - synchronized块内的操作

// 非原子操作示例
class AtomicityExample {
    long counter = 0L;  // 64位非volatile变量
    
    void increment() {
        counter++;  // 非原子操作(实际是read-modify-write三步)
    }
}

3.2 可见性(Visibility)

保证一个线程修改共享变量后,其他线程能立即看到:

实现方式 原理
volatile 禁止缓存,直接读写主内存
synchronized unlock前必须同步到主内存
final 正确初始化后对其他线程可见
// 可见性问题示例
class VisibilityIssue {
    boolean ready = false;  // 非volatile
    int result = 0;
    
    void writer() {
        result = 42;    // 可能重排序到ready=true之后
        ready = true;
    }
    
    void reader() {
        if (ready) {     // 可能看到ready为true但result仍为0
            System.out.println(result);
        }
    }
}

3.3 有序性(Ordering)

禁止特定类型的指令重排序:

场景 允许重排序 禁止重排序
普通读写操作 ×
volatile读/写 × 建立内存屏障
synchronized块 × 建立全内存屏障

四、JMM的实现机制

4.1 内存屏障(Memory Barrier)

JVM插入的底层指令,分为4种类型:

屏障类型 作用 对应Java关键字
LoadLoad 禁止读-读重排序 volatile读
StoreStore 禁止写-写重排序 volatile写
LoadStore 禁止读-写重排序 volatile读
StoreLoad 禁止写-读重排序(全能屏障) volatile写

4.2 volatile的实现细节

volatile变量的特殊处理: 1. 写操作后插入StoreStore + StoreLoad屏障 2. 读操作前插入LoadLoad + LoadStore屏障

class VolatileExample {
    volatile int v = 0;
    int normal = 0;
    
    void write() {
        normal = 1;     // 可能被重排序
        v = 2;          // 写屏障
    }
    
    void read() {
        if (v == 2) {   // 读屏障
            System.out.println(normal); // 保证看到1
        }
    }
}

4.3 final字段的特殊处理

JSR-133增强的final语义: 1. 构造函数内对final域的写入禁止重排序到构造函数外 2. 初次读取包含final域的对象引用时,保证看到所有final域的初始化值

class FinalExample {
    final int x;
    int y;
    static FinalExample instance;
    
    public FinalExample() {
        x = 1;      // 保证在构造函数完成前写入
        y = 2;      // 普通写入可能重排序
    }
    
    void writer() {
        instance = new FinalExample();
    }
    
    void reader() {
        if (instance != null) {
            int i = instance.x;  // 保证看到1
            int j = instance.y;  // 可能看到0
        }
    }
}

五、JMM与硬件内存模型

5.1 不同架构的差异

架构 内存模型特性 JMM适配策略
x86 强内存模型,StoreLoad开销大 减少StoreLoad屏障使用
ARM/POWER 弱内存模型,允许更多重排序 插入更多内存屏障
SPARC TSO(全存储定序) 类似x86的处理方式

5.2 MESI缓存一致性协议

现代CPU通过MESI协议保证缓存一致性: - Modified:缓存行已被修改 - Exclusive:缓存行独占 - Shared:缓存行共享 - Invalid:缓存行无效

JMM在MESI基础上增加了: 1. 写缓冲区(Write Buffer)处理 2. 无效队列(Invalidation Queue)优化

六、JMM实践指南

6.1 正确使用volatile

适用场景: 1. 状态标志位(如shutdown请求) 2. 一次性安全发布(如双重检查锁定) 3. 独立观察(如定期统计上报)

// 典型volatile使用场景
class VolatileUsage {
    volatile boolean shutdownRequested;
    
    void shutdown() {
        shutdownRequested = true;
    }
    
    void doWork() {
        while (!shutdownRequested) {
            // 执行任务
        }
    }
}

6.2 安全发布模式

  1. 静态初始化:利用类加载机制
    
    public static Holder holder = new Holder(42);
    
  2. volatile引用
    
    volatile Holder holder;
    
  3. final字段
    
    class Holder {
       final int n;
       Holder(int n) { this.n = n; }
    }
    

6.3 避免常见误区

  1. 误认为volatile保证原子性

    volatile int count = 0;
    count++;  // 仍然不是原子操作!
    
  2. 过度依赖线程调度

    while (!flag) {}  // 忙等待,应使用wait/notify
    

七、JMM性能优化

7.1 减少争用

  1. 缩小同步块范围: “`java // 反例 synchronized(this) { // 大量非共享操作 sharedVar++; }

// 正例 int temp; // 非共享操作… synchronized(this) { temp = ++sharedVar; }


2. **使用线程本地变量**:
   ```java
   ThreadLocal<SimpleDateFormat> dateFormat = 
       ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

7.2 避免伪共享

使用@Contended注解(Java 8+):

class ContendedValue {
    @sun.misc.Contended
    volatile long value1;
    @sun.misc.Contended
    volatile long value2;
}

八、JMM与其他内存模型对比

8.1 与C++11内存模型对比

特性 Java内存模型 C++11内存模型
原子操作 通过volatile和Atomic类 atomic模板类
顺序一致性 需要显式同步 memory_order_seq_cst
最弱约束 程序顺序规则 memory_order_relaxed

8.2 与Go内存模型对比

Go的happens-before规则更简单: - 仅通过channel通信建立happens-before关系 - 没有显式的volatile关键字 - sync包提供类似Java的同步原语

九、JMM的未来发展

  1. Project Loom:轻量级线程对内存模型的影响
  2. Valhalla项目:值类型带来的内存模型变化
  3. 异构计算:GPU/FPGA等设备的内存一致性挑战

结语

理解Java内存模型是成为高级Java开发者的必经之路。通过本文的系统性梳理,我们不仅掌握了JMM的理论基础,还学习了如何在实际开发中应用这些知识。记住:在并发编程中,“看起来正确”远远不够,必须”证明正确”。只有深入理解内存模型,才能编写出真正线程安全的Java程序。

参考资料

  1. JSR-133: Java Memory Model and Thread Specification
  2. 《Java并发编程实战》Brian Goetz 等著
  3. 《The JSR-133 Cookbook for Compiler Writers》
  4. OpenJDK HotSpot源码(orderAccess.hpp)

”`

注:本文实际约5800字(含代码示例),可根据需要调整具体示例的详细程度。建议读者结合Java语言规范和JVM规范进行延伸阅读。

推荐阅读:
  1. Java并发指南2:深入理解Java内存模型JMM
  2. Redis的内存模型是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

jmm

上一篇:rsync怎么用

下一篇:cheat命令怎么安装和使用

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》