Java volatile如何实现禁止指令重排

 更新时间:2020年11月04日 11:16:51   作者:柒  
这篇文章主要介绍了Java volatile如何实现禁止指令重排,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令重排,一般分为以下三种:

源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系统的重排 -> 最终执行指令

单线程环境里面确保最终执行结果和代码顺序的结果一致

处理器在进行重排序时,必须要考虑指令之间的数据依赖性

多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。

指令重排 - example 1

public void mySort() {
	int x = 11;
	int y = 12;
	x = x + 5;
	y = x * x;
}

按照正常单线程环境,执行顺序是 1 2 3 4

但是在多线程环境下,可能出现以下的顺序:

2 1 3 4

1 3 2 4

上述的过程就可以当做是指令的重排,即内部执行顺序,和我们的代码顺序不一样

但是指令重排也是有限制的,即不会出现下面的顺序

4 3 2 1

因为处理器在进行重排时候,必须考虑到指令之间的数据依赖性

因为步骤 4:需要依赖于 y的申明,以及x的申明,故因为存在数据依赖,无法首先执行

例子

int a,b,x,y = 0

线程1 线程2
x = a; y = b;
b = 1; a = 2;
x = 0; y = 0

因为上面的代码,不存在数据的依赖性,因此编译器可能对数据进行重排

线程1 线程2
b = 1; a = 2;
x = a; y = b;
x = 2; y = 1

这样造成的结果,和最开始的就不一致了,这就是导致重排后,结果和最开始的不一样,因此为了防止这种结果出现,volatile就规定禁止指令重排,为了保证数据的一致性

指令重排 - example 2

比如下面这段代码

public class ResortSeqDemo {
  int a= 0;
  boolean flag = false;

  public void method01() {
    a = 1;
    flag = true;
  }

  public void method02() {
    if(flag) {
      a = a + 5;
      System.out.println("reValue:" + a);
    }
  }
}

我们按照正常的顺序,分别调用method01() 和 method02() 那么,最终输出就是 a = 6

但是如果在多线程环境下,因为方法1 和 方法2,他们之间不能存在数据依赖的问题,因此原先的顺序可能是

a = 1;
flag = true;

a = a + 5;
System.out.println("reValue:" + a);

但是在经过编译器,指令,或者内存的重排后,可能会出现这样的情况

flag = true;

a = a + 5;
System.out.println("reValue:" + a);

a = 1;

也就是先执行 flag = true后,另外一个线程马上调用方法2,满足 flag的判断,最终让a + 5,结果为5,这样同样出现了数据不一致的问题

为什么会出现这个结果:多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。

这样就需要通过volatile来修饰,来保证线程安全性

Volatile针对指令重排做了啥

Volatile实现禁止指令重排优化,从而避免了多线程环境下程序出现乱序执行的现象

首先了解一个概念,内存屏障(Memory Barrier)又称内存栅栏,是一个CPU指令,它的作用有两个:

保证特定操作的顺序保证某些变量的内存可见性(利用该特性实现volatile的内存可见性)

由于编译器和处理器都能执行指令重排的优化,如果在指令键插入一条Memory Barrier则会告诉编译器和CPU,不管什么指令都不能和这条Memory Barrier指令重排序,也就是说,通过插入内存屏障前后的指令执行重排序优化。内存屏障另外一个作用是刷新出各种CPU的缓存数,因此任何cpu上的线程都能读取到这些数据的最新版本


也就是在Volatile的写和读的时候,加入屏障,防止出现指令重排线程安全得到保证

工作内存与主内存同步延迟现象导致的可见性问题

  • 可以使用synchronized或volatile关键字解决,它们都可以使得一个线程修改后的变量立即对其他线程可见。
  • 对于指令重排导致的可见性问题和有序性问题
  • 可以利用volatile关键字解决,因为volatile的另一个作用就是禁止重排序优化。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • java实现zip,gzip,7z,zlib格式的压缩打包

    java实现zip,gzip,7z,zlib格式的压缩打包

    本文是利用Java原生类和apache的commons实现zip,gzip,7z,zlib的压缩打包,如果你要是感兴趣可以进来了解一下。
    2016-10-10
  • Spring注入Bean的一些方式总结

    Spring注入Bean的一些方式总结

    这篇文章主要给大家总结介绍了关于Spring注入Bean的一些方式,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04
  • Mybatis自定义拦截器和插件开发详解

    Mybatis自定义拦截器和插件开发详解

    这篇文章主要给大家介绍了关于Mybatis自定义拦截器和插件开发的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • mybatis-plus 处理大数据插入太慢的解决

    mybatis-plus 处理大数据插入太慢的解决

    这篇文章主要介绍了mybatis-plus 处理大数据插入太慢的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • java 爬虫详解及简单实例

    java 爬虫详解及简单实例

    这篇文章主要介绍了java 爬虫详解及简单实例的相关资料,需要的朋友可以参考下
    2017-05-05
  • mybatis 字段名自动转小写的实现

    mybatis 字段名自动转小写的实现

    这篇文章主要介绍了mybatis 字段名自动转小写的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Java substring方法实现原理解析

    Java substring方法实现原理解析

    这篇文章主要介绍了Java substring方法实现原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • Activiti流程文件部署过程解析

    Activiti流程文件部署过程解析

    这篇文章主要介绍了Activiti流程文件部署过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • Java Spring快速入门

    Java Spring快速入门

    本文主要介绍了SpringSpring简介和入门知识。具有一定的参考价值,下面跟着小编一起来看下吧
    2017-01-01
  • java通过jni调用opencv处理图像的方法

    java通过jni调用opencv处理图像的方法

    今天小编就为大家分享一篇java通过jni调用opencv处理图像的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08

最新评论