二:并发编程-重排序

上一章说了happens-before原则和as-if-serial 语义,也举了一个单线程的例子说明happens-before仅仅是要求前一个操做的执行结果对后一个操做可见,而且前一个操做要在后一个操做以前。程序员

同时as-if-serial语义保证了在单线程程序中,无论怎样重排序,都不会对最终结果产生影响。缓存

as-if-serial 语义把单线程程序保护起来,遵循as-if-serial语义的编译器,runtime和处理器共同为编写单线程的程序员创造了一个幻觉,认为程序就是在顺序执行,as-if-serial 语义使单线程程序不用担忧重排序的困扰,也无需担忧内存可见性的问题。多线程

既然重排序既然对单线程没有影响,咱们看一下重排序对多线程有什么影响:app

一个典型的例子就是在get 和set操做中,若是多线程同事去get和set以下:线程

class TestGetSet{排序

    int a = 0;内存

    boolean flag = false;get

    public void set(){编译器

        a=1;    //1编译

        flag = true;   //2

    }

    public int get(){

        if(flag)  // 3

            return a * a;   //4

    }

}

如今假若有两个线程,分别线程A和线程B,线程A负责进行set,线程B负责进行get,那么线程B在执行操做4的时候,可否看到线程A在操做1中对共享变量的改变,答案是不必定

因为操做1和操做2没有依赖关系,线程会对操做1和操做2进行重排序,操做3和操做4一样也没有依赖关系,也会进行重排序,那么看一下当操做1和操做2进行重排序后:

在上图中,就是操做1和操做2进行了重排序,线程A在执行的时候,先把变量设置成了true,随后线程B读取这个变量,因为条件为真,进行下一步操做,可是此刻线程A尚未把共享变量写入到主内存中,那么线程B读取到的结果就是脏数据,多线程程序的语义就被破坏了

再看一下当操做3和操做4被重排序以后:

在程序中,操做3和操做4存在控制依赖关系,当代码中存在控制依赖关系的时候,会影响到指令序列执行的并行度。为此编译器核处理器会采起猜想执行客服控制相关性对并行度的影响。已处理器的才作为例,执行线程 B的处理器能够提早读取并计算a*a,而后把计算结果临时保存在一个叫重排序缓冲的硬件缓存中。当接下来判断条件为真的时候,再把计算结果写入变量中。

从上咱们能够看出来,其实就是对操做3和操做4进行了重排序,重排序在这里破话了多线程程序的语义!

在单线程的程序中,runtime和处理器遵循as-if-serial原则,即便重排序也不会对计算结果产生影响,可是在多线程中,对存在控制依赖关系的操做进行重排序,可能会改变程序的执行结果。

相关文章
相关标签/搜索