Skip to content

06_Happens-Before规则.md

Happens-Before 规则是 Java 内存模型(Java Memory Model, JMM)的核心概念之一,用于定义多个线程之间操作的可见性和顺序性。它规定了在多线程环境中,一个操作的结果如何对其他线程可见。理解 Happens-Before 规则对于编写正确的并发程序至关重要。

Happens-Before 规则的定义

如果操作 A happens-before 操作 B,那么操作 A 的结果在操作 B 中是可见的,并且操作 A 的执行顺序在操作 B 之前。需要注意的是,Happens-Before 是一个偏序关系,即它并不要求在程序中的所有操作之间都必须建立这种关系。

Happens-Before 规则的几条重要规定

  • 程序次序规则(Program Order Rule)

在一个线程内,按照程序代码的顺序,前面的操作 happens-before 于后面的操作。即使在代码中前面的操作已经执行完毕,后面的操作才会开始。

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

一个线程对一个锁的解锁操作 happens-before 于另一个线程对同一个锁的加锁操作。锁的释放和获取之间建立了 happens-before 关系。

  • volatile 变量规则(Volatile Variable Rule)

对一个 volatile 变量的写操作 happens-before 于后续对这个 volatile 变量的读操作。这确保了对 volatile 变量的读写具有可见性。

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

在一个线程中对另一个线程的 start() 方法的调用 happens-before 于该线程中的任何操作。

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

一个线程中的所有操作 happens-before 于另一个线程检测到该线程已经终止(通过 Thread.join() 或 Thread.isAlive() 返回结果)。

  • 线程中断规则(Thread Interruption Rule)

对线程 interrupt() 方法的调用 happens-before 于被中断线程检测到中断事件的发生(通过 Thread.interrupted() 或 Thread.isInterrupted())。

  • 对象终结规则(Finalizer Rule)

一个对象的构造函数执行完毕 happens-before 于该对象的 finalize() 方法的开始。

  • 传递性(Transitivity)

如果操作 A happens-before 操作 B,且操作 B happens-before 操作 C,那么操作 A happens-before 操作 C。

Happens-Before 规则的实际应用

理解并应用 Happens-Before 规则,可以帮助我们确保多线程程序的正确性和线程安全性。以下是几个示例,展示了如何应用这些规则:

java
public class SynchronizedExample {
    private int value = 0;

    public synchronized void increment() {
        value++;
    }

    public synchronized int getValue() {
        return value;
    }

    public static void main(String[] args) {
        SynchronizedExample example = new SynchronizedExample();

        Thread t1 = new Thread(example::increment);
        Thread t2 = new Thread(() -> System.out.println(example.getValue()));

        t1.start();
        t2.start();
    }
}

在这个例子中,increment 和 getValue 方法都被 synchronized 修饰,确保了对 value 的读写操作之间的 happens-before 关系,保证了线程安全。

  • 示例 2: 使用 volatile 变量
java
public class VolatileExample {
    private volatile boolean flag = false;

    public void setFlag() {
        flag = true;
    }

    public void doWork() {
        while (!flag) {
            // 等待 flag 变为 true
        }
        System.out.println("Flag is true, proceeding with work.");
    }

    public static void main(String[] args) {
        VolatileExample example = new VolatileExample();

        Thread t1 = new Thread(example::doWork);
        Thread t2 = new Thread(example::setFlag);

        t1.start();
        t2.start();
    }
}

在这个例子中,setFlag 方法对 flag 变量的写操作 happens-before doWork 方法对 flag 变量的读操作,确保了线程之间的可见性。

总结

  • Happens-Before 规则是 Java 并发编程中确保操作顺序和可见性的基本原则。通过理解和应用这些规则,我们可以编写出更为正确和高效的多线程程序。关键点在于:
    • 理解程序次序规则、监视器锁规则、volatile 变量规则等基本规则。
    • 理解和应用这些规则来确保线程间操作的正确性和可见性。
    • 利用这些规则实现线程安全的代码,避免并发问题。
    • 通过遵循 Happens-Before 规则,我们可以确保在多线程环境中,线程间的操作是有序且可见的,从而提高程序的正确性和可靠性。