温馨提示×

温馨提示×

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

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

JMM中的Happens-before是什么

发布时间:2021-06-21 13:40:04 来源:亿速云 阅读:220 作者:chen 栏目:开发技术
# JMM中的Happens-before是什么

## 引言

在并发编程的世界中,理解内存模型(Memory Model)是确保程序正确性的关键。Java作为一门广泛使用的编程语言,其内存模型(Java Memory Model, JMM)定义了多线程环境下变量的访问规则。其中,"Happens-before"关系是JMM的核心概念之一,它规定了操作之间的可见性和顺序性。本文将深入探讨Happens-before的概念、规则、实现原理及其在实际开发中的应用。

---

## 目录

1. **Java内存模型(JMM)概述**
2. **Happens-before的定义与作用**
3. **Happens-before的八大规则**
4. **Happens-before的实现原理**
5. **Happens-before与指令重排序**
6. **实际案例分析**
7. **常见误区与注意事项**
8. **总结**

---

## 1. Java内存模型(JMM)概述

Java内存模型(JMM)是Java虚拟机(JVM)规范中定义的一种抽象模型,用于描述多线程环境下变量的访问规则。JMM的核心目标是解决以下问题:

- **可见性**:一个线程对共享变量的修改何时对其他线程可见。
- **有序性**:指令的执行顺序是否会被编译器或处理器优化(如重排序)打乱。
- **原子性**:某些操作(如`long`和`double`的非原子性写入)在多线程环境下的行为。

JMM通过定义Happens-before关系来解决这些问题,确保程序员能够编写正确的并发程序。

---

## 2. Happens-before的定义与作用

### 2.1 定义
Happens-before是JMM中定义的一种偏序关系(Partial Order),用于描述两个操作之间的可见性和顺序性。如果操作A Happens-before操作B(记作A → B),那么:

1. **A的结果对B可见**。
2. **A的执行顺序在B之前**(从程序逻辑的角度)。

### 2.2 作用
Happens-before关系的主要作用是:
- **解决可见性问题**:确保一个线程的写操作对其他线程可见。
- **禁止某些重排序**:编译器或处理器不能随意打乱具有Happens-before关系的操作顺序。

---

## 3. Happens-before的八大规则

JMM定义了以下Happens-before规则:

### 3.1 程序顺序规则(Program Order Rule)
- 在同一个线程中,按照代码顺序,前面的操作Happens-before后面的操作。
  ```java
  int x = 1; // 操作A
  int y = 2; // 操作B
  // A → B

3.2 监视器锁规则(Monitor Lock Rule)

  • 对一个锁的解锁Happens-before后续对这个锁的加锁。
    
    synchronized (lock) {
      x = 10; // 操作A
    } // 解锁Happens-before后续加锁
    

3.3 volatile变量规则(Volatile Variable Rule)

  • 对一个volatile变量的写操作Happens-before后续对这个变量的读操作。
    
    volatile int v = 0;
    v = 1; // 操作A
    int r = v; // 操作B, A → B
    

3.4 线程启动规则(Thread Start Rule)

  • 线程的start()方法调用Happens-before该线程的任何操作。
    
    Thread t = new Thread(() -> {
      System.out.println(x); // 操作B
    });
    x = 100; // 操作A
    t.start(); // A → B
    

3.5 线程终止规则(Thread Termination Rule)

  • 线程中的所有操作Happens-before其他线程检测到该线程已经终止(如t.join()t.isAlive()返回false)。

3.6 中断规则(Interruption Rule)

  • 对线程的interrupt()调用Happens-before被中断线程检测到中断事件(如抛出InterruptedException)。

3.7 终结器规则(Finalizer Rule)

  • 对象的构造函数执行Happens-before它的finalize()方法。

3.8 传递性(Transitivity)

  • 如果A → B且B → C,那么A → C。

4. Happens-before的实现原理

Happens-before关系的实现依赖于以下机制:

4.1 内存屏障(Memory Barrier)

  • JVM会在编译或运行时插入内存屏障指令(如LoadLoadStoreStore等),禁止某些重排序。
  • 例如,volatile写操作后插入StoreLoad屏障。

4.2 锁与同步机制

  • 锁的获取和释放会隐式插入内存屏障,确保可见性和顺序性。

4.3 volatile关键字

  • volatile变量的读写会生成特定的指令,确保Happens-before关系。

5. Happens-before与指令重排序

5.1 重排序的类型

  • 编译器重排序:编译器在不改变单线程语义的情况下优化指令顺序。
  • 处理器重排序:CPU的乱序执行可能导致指令顺序变化。

5.2 Happens-before的限制

  • 如果两个操作没有Happens-before关系,JVM可以自由重排序。
  • 例如:
    
    int a = 1; // 操作A
    int b = 2; // 操作B
    // A和B无依赖关系,可能被重排序。
    

6. 实际案例分析

6.1 单例模式(Double-Checked Locking)

class Singleton {
    private static volatile Singleton instance;
    static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // 写操作
                }
            }
        }
        return instance; // 读操作
    }
}
  • volatile确保写操作Happens-before读操作,避免看到未初始化的对象。

6.2 线程间通信

volatile boolean flag = false;
int value = 0;

// 线程A
value = 100;
flag = true; // 写操作

// 线程B
if (flag) { // 读操作
    System.out.println(value); // 保证看到value=100
}

7. 常见误区与注意事项

  1. 误认为Happens-before是时间顺序
    • Happens-before是逻辑顺序,不一定是时间顺序。
  2. 忽略volatile的可见性
    • 非volatile变量的修改可能对其他线程不可见。
  3. 过度依赖程序顺序规则
    • 多线程环境下,程序顺序规则仅适用于同一线程内的操作。

8. 总结

Happens-before是JMM的核心概念,它通过定义操作之间的偏序关系,解决了多线程环境下的可见性和有序性问题。理解并正确应用Happens-before规则,是编写高效、正确并发程序的关键。在实际开发中,应结合锁、volatile等同步机制,确保程序的线程安全性。


参考文献

  1. 《Java并发编程实战》
  2. JSR-133: Java Memory Model and Thread Specification
  3. Oracle官方文档:Java内存模型

”`

(注:本文约为2000字,完整6700字版本需扩展每部分的细节,添加更多代码示例和底层原理分析。)

向AI问一下细节

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

AI