一文让你理解as-if-serila和happens-before语义

概述

本文参考《Java并发编程的艺术》一书,温故而知新,加深对基础的理解。

一、指令序列的重排序

我们在编写代码的时候,通常自上而下编写,那么希望执行的顺序,理论上也是逐步串行执行,但是为了提高性能,编译器和处理器常常会对指令做重排序。从Java源码到最终实际的指令,需要经过三个阶段的重排序:

1) 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
2) 指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
3) 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

JMM属于语言级别的内存模型,他确保在不同的编译器和不同的处理器平台上,通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。

二、as-if-serial语义

as-if-serial语义的含义是:无论如何重排序,单线程程序的执行结果不能被改变。编译器和处理器都必须遵循as-if-serial语义,编译器和处理器为了遵循这一易于,他们就不会对存在数据依赖关系的操作进行重排序。具体来说就是如下表的操作就不会重排序:

as-if-serial语义把单线程程序保护了起来,遵守as-if-serial语义的编译器、runtime和处理器共同为编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的。

三、happens-before规则

happens-before是JMM的核心概念,通过happens-before规则JMM可以实现:

  • 一方面,为程序员提供了足够强的内存可见性保证
  • 另一方面,对编译器和处理器的限制要尽可能的宽松
1、happens-before规则

JSR-133规范中定义了如下happens-before规则:

(1)程序顺序规则:一个线程中的每个操作,happens-brfore于该线程中的任意后序操作。

(2)监视器锁规则:线程对解锁obj之前的对变量的写,对接下来对obj加锁的线程对变量的读可见。

static int x=0;
static final Object obj=new Object();

new Thread(()->{
    synchronized(obj){
        x=1;
    }
},"t1").start();

new Thread(()->{
    synchronized(obj){
        //t1对x的修改对于t2是可见的,因此将会打印2
        System.out.println(x++);
    }
},"t2").start();

(3)volatile变量规则:一个线程对volatile的写,对接下其他变量对该变量的读可见。

volatile static int x=0;

new Thread(()->{
    synchronized(obj){
        x=10;
    }
},"t1").start();

new Thread(()->{
    synchronized(obj){
        //t1对x的修改对于t2是可见的,因此将会打印10
        System.out.println(x);
    }
},"t2").start();

(4)传递性:如果A happens-before B,且 B happens-before C,那么 A happens-before C。

(5)start()规则:线程start()之前对变量的写,对该变量开始后对该变量可见。

static int x;

x=10;

new Thread(()->{
    synchronized(obj){
        //t1启动之前对x的写对它是可见的,因此将会打印10
        System.out.println(x);
    }
},"t1").start();

(6)join()规则:线程结束前对变量的写,对其他线程得知他结束后的读可见。(比如线程调用isAlive()、join())

static int x;

Thread t1=new Thread(()->{
    synchronized(obj){
        x=10;
    }
},"t1");

t1.start();
t1.join();

//t1启动之前对x的写对它是可见的,因此将会打印10
System.out.println(x);

总的来说:happens-before和as-if-serial语义是一回事

  • as-if-serial语义保证单线程内程序的执行结果不被重排序改变,hapeens-before关系保证正确同步的多线程程序的执行结果不被重排序改变。
  • as-if-serial语义给编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的。happens-before规则给编写正确同步的多线程程寻的程序员一个幻觉:正确同步的多线程程序是按happens-before指定的顺序来执行的。

留言区

还能输入500个字符