欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

指令重排序和内存屏障,屏障指令

来源: javaer 分享于  点击 15915 次 点评:24

指令重排序和内存屏障,屏障指令


一、指令重排序

  指令重排序分为三种,分别为编译器优化重排序指令级并行重排序内存系统重排序。如图所示,后面两种为处理器级别(即为硬件层面)。

// CPU1执行以下操作 a = 1; int i = b; // CPU2执行下面操作 b = 1; int j = a;

其执行图如下:
    

 

     从上面图中我们可以看到,对于CPU来说,先将a = 1写入缓存在读取变量b,过后在写入a到主内存,而这个操作从表面上看就变成了先读取变量b,在写入a到主内存,也就是发生了重排序,所以才说这为伪重排序。

        而从上面我们也可以看出,由于CPU1和2写入的时机不同,最终可能导致读到的(a,b)变量有四种情况,分别是(0,0),(0,1),(1,0),(1,1)。例如,在两个缓存未写入主内存的时候就进行变量读取,这时候读到的就为(0,0),其他情况类推。所以Java在实现内存模型的时候会禁止特定类型的重排序。

 

   as-if-serial语义:这是重排序都需要遵循的规则,其大致意思就是在单线程中,只要不改变程序的最终执行结果,那么为了提升性能可以改变指令执行的顺序。

二、内存屏障

  在编译器方面使用volatile关键字可以禁止指令重排序,而在硬件方面实现禁止指令重排序的则是内存屏障。其中包括硬件层本来就有的LoadBarriers和StoreBarriers 和JVM封装实现的四种内存屏障。

   从硬件层上

    内存屏障分为两种,LoadBarriers和StoreBarriers。

    • LoadBarriers:在执行屏障后一个操作前,保证已经刷新了缓存的数据,也就是说使缓存失效,强制从内存刷新数据到缓存中。
      i = a;
      LoadBarriers;
      // ..其他操作

      如上伪代码中,在执行其他操作之前必须保证a的变量从主内存中读取并且刷新到缓存中。

    • StoreBarriers:此屏障之前的写入缓存中的数据同步到内存中,并且保证其他线程可见。
      a = 1;
      b = 2;
      c = 3;
      StoreBarriers;
      // ..其他操作

      如上伪代码中,保证在其他操作之前,写入缓存中的a,b,c三个变量同步到主内存中,并且其他线程可以观察到变量的变化。

  JVM实现的内存屏障

  volatile的禁止指令重排序

    我们都知道volatile关键字有两个语义:

    • 保证内存可见性
    • 禁止指令重排序

    其中JVM对其禁止指令重排序在硬件层面的实现就是通过在volatile修饰的变量前后插入内存屏障。volatile变量的内存屏障规则如下:

  在每个volatile写操作前插入StoreStore屏障,在写操作后插入StoreLoad屏障;
  在每个volatile读操作前插入LoadLoad屏障,在读操作后插入LoadStore屏障;

    而在编译器方面则是因为对于volatile变量内存中的六种操作会有特殊的规则,可以看看我的另一篇文章——浅谈内存模型,里面介绍了volatile两种语义的原理,同时也说明了volatile关键字没有原子性的原因。

 

 

 文章若有不正之处,还望指出,在此多谢!

相关文章

    暂无相关文章
相关栏目:

用户点评