什么是happens-before原则 ?

一、定义

happens-before原则是一种用于描述多线程程序中操作执行顺序的规则。它是Java内存模型(Java Memory Model,JMM)的一部分:

如果一个操作 A “happen-before” 另一个操作 B,那么 A 的结果对 B 是可见的。这个概念是理解线程间内存可见性的关键。


举一个例子,如以下代码:

public class ThreadStartExample {

    private int startValue = 10;

    public void startNewThread() {
        startValue +=1;
        new Thread(() -> {
            int localValue = startValue;  
        }).start();
    }
}

有两个线程,一个写startValue,一个读startValue,但是我们并没有用synchronized加锁,也没有用volatile修饰,那么JVM是如何保证,在主线程中修改startValue的操作在子线程中是可见的呢 ?


这其实就是happens-before原则发挥的作用了。但是,happens-before原则也不是没有任何限制,任何场景都能happens-before的,还是有一些规则要求的。


二、happends-before规则


1、程序顺序规则(Program Order Rule)

程序顺序规则(Program Order Rule):在单个线程内,按照程序代码的顺序,前一个操作 happens-before 后一个操作。

public class ProgramOrderExample {
    private int a = 0;
    private int b = 0;

    public void method() {
        a = 1;  // 操作1
        b = 2;  // 操作2,
        // 操作1 `happens-before` 操作2
    }
}


2、锁规则(Monitor Lock Rule)

锁规则(Monitor Lock Rule):对一个锁的解锁 happens-before 随后对这个锁的加锁。即在 synchronized 代码块或方法中,释放锁之前的所有操作对于下一个获取这个锁的线程是可见的。

public class MonitorLockExample {
    private int value = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized(lock) {
            value++;  // 操作在锁内
        }
    }

    public int getValue() {
        synchronized(lock) {
            return value;  
        }
    }
}

increment 方法中对 value 的修改,在 getValue 方法获取锁之后是可见的。


3、volatile 变量规则(Volatile Variable Rule)

volatile 变量规则(Volatile Variable Rule):对一个 volatile 字段的写操作 happens-before 任意后续对这个字段的读操作。即确保 volatile 变量的写操作对其他线程立即可见。

public class VolatileExample {
    private volatile boolean flag = false;

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

    public boolean checkFlag() {
        return flag;  
    }
}

当一个线程调用 writeFlag(),另一个线程随后调用 checkFlag() 将看到 flag 为 true


4、线程启动规则(Thread Start Rule)

线程启动规则(Thread Start Rule):对线程的 start() 方法的调用 happens-before 该线程的每个动作。确保线程启动时,主线程中对共享变量的写操作对于新线程是可见的

public class ThreadStartExample {

    private int startValue = 10;

    public void startNewThread() {
        startValue +=1;
        new Thread(() -> {
            int localValue = startValue; 
            // 处理 localValue
        }).start();
    }
}

线程启动时,将看到 startValue 的值为 11


5、线程终止规则(Thread Termination Rule)

线程终止规则(Thread Termination Rule):一个线程的所有操作 happens-before 对这个线程的 join() 方法的成功返回。确保线程终止时,该线程中的所有操作对于调用 join() 方法的线程是可见的

public class ThreadJoinExample {
    private int counter = 0;

    public void incrementInThread() throws InterruptedException {
        Thread thread = new Thread(() -> {
            counter++;
        });
        thread.start();
        thread.join();  // `happens-before` counter 的读操作
        int value = counter;  // 这里能够看到线程中对 counter 的修改
    }
}

主线程中可以看到子线程对counter的修改


6、线程中断规则(Thread Interruption Rule)

线程中断规则(Thread Interruption Rule): 对线程的 interrupt() 方法的调用 happens-before 被中断线程检测到中断事件的发生。即线程的中断操作在被该线程检测到之前已经发生


7、对象终结规则(Finalizer Rule)

对象终结规则(Finalizer Rule): 一个对象的初始化完成(构造函数执行结束)happens-before 它的 finalize() 方法的开始。即在对象被回收前,其构造过程已经完全结束

public class FinalizerExample {

    private int value;

    public FinalizerExample() {
        value = 10;  // 构造函数中的操作
    }

    @Override
    protected void finalize() {
        // 此方法 `happens-before` 构造函数中的操作
        if (value != 10) {
            // 这里不应该发生
        }
    }
}


8、传递性(Transitivity)

传递性(Transitivity):如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那就可以得出操作 A 先行发生于操作 C 的结论

public class TransitivityExample {
    private volatile boolean ready;
    private int number;

    public void writer() {
        number = 42;       // 操作 A
        ready = true;      // 操作 B
    }

    public void reader() {
        if (ready) {              // 操作 C(由于 ready 是 volatile,它 `happens-before` 这里的操作)
            assert number == 42;  // 因为 A `happens-before` B,B `happens-before` C,所以 A `happens-before` C
        }
    }
}

由于 ready 是一个 volatile 变量,写入 ready(操作 B)发生在读取 ready 之前,同样,写入 number(操作 A)发生在写入 ready 之前。根据传递性规则,写入 number 发生在读取 ready 之前

posted @ 2025-06-03 20:18  jock_javaEE  阅读(100)  评论(0)    收藏  举报