目录

「002」 happens-before 原则

happens-before 原则是判断数据是否存在竞争,线程是否安全的主要依据,保证了多线程环境下变量的可见性,是 JMM 中非常重要的一部分。

定义

happens-before 的原因是

  1. 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。即保证程序顺序执行的最终结果,但并不保证程序顺序执行的指令序列,编译器和处理器会对指令序列进行重排序,但重排序不影响最终结果,这也是多线程线程安全无法保证的原因。
  2. 锁定规则:一个 unlock 操作发生在后面对同一个锁的 lock 操作之前。lock 操作必须作用于一个未被其他线程锁住的变量,unlock 操作必须作用于一个被该线程锁住的变量,在面对同一个锁中的 unlock-lock 序列,unlock 必须发生在 lock 之前
  3. volatile变量规则:对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。
  4. 传递规则:如果操作 A 先行发生于操作 B,而操作 B 又先行发生于操作 C,则可以得出操作 A 先行发生于操作 C。
  5. 线程启动规则:Thread 对象的 start() 方法先行发生于此线程的每个一个动作;
  6. 线程中断规则:对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
  7. 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过 Thread.join() 方法结束、Thread.isAlive() 的返回值手段检测到线程已经终止执行。
  8. 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始。

https://leslie-cloud.oss-cn-beijing.aliyuncs.com/2021/03/6d82479d871d.png

符合任意一条规则的两个操作之间存在 happens-before 关系。

内容

happens-before 的结果是

  1. 如果一个操作 happens-before 另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
  2. 两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。

样例

下面是一个典型的不满足 happens-before 规则的样例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {

    public static void main(String[] args) {
	    Thread a = new Thread(() -> write(1));
        Thread c = new Thread(() -> write(3));
        Thread b = new Thread(() -> System.out.println(read()));

        c.run();
        b.run();
        a.run();
    }

    private static int i = 0;

    public static void write(int j) {
        i = j;
    }

    public static int read() {
        return i;
    }
}

a 线程执行写 1,b 线程执行读,c 线程执行写 3。由于是不同线程的操作,所以不满足程序次序规则,没有锁和 volatile 变量,所以不满足锁定规则和 volatile 变量规则,没有任何 happens-before 规则,所以不满足传递规则,后四条毫无疑问不满足。因此这三个线程的顺序是无法得到保证的,最终的结果同样无法确定。

如果需要确定的结果,只需要为它加上锁使其符合锁定规则,或者将 i 加上 volatile 修饰,使其满足 volatile 变量规则即可。

总结

总的来说,如果两个操作之间不符合原因中的任意一条,那么可以说这两个操作不是 happens-before 的,JVM 将可以对这两个操作进行重排序,并不保证最终结果的顺序。如果 A 操作 happens-before B 操作,那么 A 操作的任何结果、在内存上的任何修改对 B 操作都是可见的。

如果想使一个不存在 happens-before 规则从而导致结果不确定的程序变得结果确定,只需要为其加上修饰使其满足任意一条规则即可。