Java中volatile防止指令重排

目录
  • 什么是指令重排?
  • 为什么指令重排能够提高性能
  • volatile是怎么禁止指令重排的?

volatile可以防止指令重排,在多线程环境下有时候我们需要使用volatile来防止指令重排,来保证代码运行后数据的准确性

什么是指令重排?

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

指令重排有以下三个特点:

1.单线程环境下指令重排后可以保证与顺序执行指令的结果一致(就是不进行指令重排的情况)

//原来的执行顺序
a=1;
b=0;
//进行指令重排后执行
b=0;
a=1;

这两个顺序执行的指令结果都是a=1,b=0

2.进行指令重排的时候要考虑指令之间的数据依赖性(某个指令的数据需要根据另一个指令的数据获得)

//原来的执行顺序
a=0;    //指令1
a=10;    //指令2
b=a+1;   //指令3

//进行指令重排后
a=0;
b=a+1;
a=10;

此时两种顺序输出的结果就不一样了,这是因为指令3的数据依赖于指令2,单线程环境下指令重排不会出现这种情况。

3.多线程环境下,多个线程交替执行,由于编译器会进行指令重排,结果无法预测。

为什么指令重排能够提高性能

串行的代码确实会按代码语意正确的执行(就是编写的代码的运行逻辑),但是编译器对于代码本身的优化却并不一定会按实际的代码一步一步的执行,就比如下面这段代码

public void process() {
    int a = 10;  #指令1
    int b = 20;  #指令2
}

代码的执行过程一定是是int a=10然后int b=20,但是代码转换成计算机可以识别的指令可能是指令2,指令1。

我们知道指令的执行可以分为这几步:

  • 取址 IF
  • 译码和取寄存器操作数 ID
  • 执行或者有效地址计算 EX (ALU逻辑计算单元)
  • 存储器访问 MEM
  • 写回 WB (寄存器)

一段代码并不是由单条指令就可以执行完毕的,而是通过流水线技术来执行多条指令。

流水线技术是一种将指令分解为多步,并让不同指令的各步操作重叠,从而实现几条指令并行处理,这样就提高了指令的执行速度

简单来说就是通过指令重排,可以使用流水线技术实现指令的细分,然后实现几条指令的并行处理,从而提高速度

volatile是怎么禁止指令重排的?

这就涉及到一个概念内存屏障(内存栅栏),它是一个cpu指令,有两个作用:

  • 保证某些特定操作的执行顺序
  • 保证某些变量的内存可见性(实现了volatile保证可见性)

编译器和处理器都可以进行指令重排,那么如果我们在程序中插入一条Memery Barrier(内存屏障),那么就会告诉编译器和cpu不能对这条指令进行重排,也就是说通过插入内存屏障,使屏障前后的指令不会进行重排优化,内存屏障还可以强制刷出cpu的缓存,因此cpu上的线程都能读到这些数据的最新版本。

简单来说就是插入内存屏障后告诉cpu和编译器,这个内存屏障前后的指令你不要给我进行重排序

内存屏障分为四种:

StoreStore屏障、StoreLoad屏障、LoadLoad屏障、LoadStore屏障。

  • Load相当于读屏障
  • Store相当于写屏障

到此这篇关于Java中volatile防止指令重排 的文章就介绍到这了,更多相关Java volatile防止指令重排 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈java指令重排序的问题

    指令重排序是个比较复杂.觉得有些不可思议的问题,同样是先以例子开头(建议大家跑下例子,这是实实在在可以重现的,重排序的概率还是挺高的),有个感性的认识 /** * 一个简单的展示Happen-Before的例子. * 这里有两个共享变量:a和flag,初始值分别为0和false.在ThreadA中先给 a=1,然后flag=true. * 如果按照有序的话,那么在ThreadB中如果if(flag)成功的话,则应该a=1,而a=a*1之后a仍然为1,下方的if(a==0)应该永远不会为 * 真,

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

    计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令重排,一般分为以下三种: 源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系统的重排 -> 最终执行指令 单线程环境里面确保最终执行结果和代码顺序的结果一致 处理器在进行重排序时,必须要考虑指令之间的数据依赖性 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测. 指令重排 - example 1 public void mySort() { i

  • Java中volatile防止指令重排

    目录 什么是指令重排? 为什么指令重排能够提高性能 volatile是怎么禁止指令重排的? volatile可以防止指令重排,在多线程环境下有时候我们需要使用volatile来防止指令重排,来保证代码运行后数据的准确性 什么是指令重排? 计算机在执行程序时,为了提高性能,编译器和处理器一般会进行指令重排,一般分为以下三种: 指令重排有以下三个特点: 1.单线程环境下指令重排后可以保证与顺序执行指令的结果一致(就是不进行指令重排的情况) //原来的执行顺序 a=1; b=0; //进行指令重排后执

  • java中volatile关键字的作用与实例代码

    一,什么是volatile关键字,作用是什么 volatile是java虚拟机提供的轻量级同步机制 ​ 作用是: 1.保证可见性 2.禁止指令重排 3.不保证原子性 本篇具体就讲解 什么叫保证了可见性, 什么叫禁止指令重排,什么是原子性 而在这之前需要对JMM 有所了解 二,什么是JMM ​ JMM(java 内存模型 Java Memory Model 简称JMM) 本身是一个抽象的概念,并不在内存中真实存在的,它描述的是一组规范或者规则,通过这组规范定义了程序中各个变量(实例字段,静态字段和

  • Java中Volatile关键字能保证原子性吗

    目录 volatile 有序性 可见性 是否能够保证原子性 volatile volatile 是 Java 中的一个相对来说比较重要的关键字,主要就是用来修饰会被不同线程访问和修改的变量. 而这个变量只能保证两个特性,一个是保证有序性,另外一个则是保证可见性. 那么什么是有序性,什么又是可见性呢? 有序性 那么什么是有序性呢? 其实程序执行的顺序按照代码的先后顺序执行,禁止进行指令重排序. 看似理所当然,其实并不是这样,指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结

  • java 中volatile和lock原理分析

    java 中volatile和lock原理分析 volatile和lock是Java中用于线程协同同步的两种机制. Volatile volatile是Java中的一个关键字,它的作用有 保证变量的可见性 防止重排序 保证64位变量(long,double)的原子性读写 volatile在Java语言规范中规定的是 The Java programming language allows threads to access shared variables (§17.1). As a rule,

  • Java中Volatile关键字详解及代码示例

    一.基本概念 先补充一下概念:Java内存模型中的可见性.原子性和有序性. 可见性: 可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉.通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情.为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制. 可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的.也就是一个线程修改的结果.另一个线程马上就能看到.比如:用volatile修饰的变量,就会具有可见性.volatile修饰的

  • 深入了解Java中Volatile关键字

    一.基本概念 先补充一下概念:Java 内存模型中的可见性.原子性和有序性. 可见性: 可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉.通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情.为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制. 可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的.也就是一个线程修改的结果.另一个线程马上就能看到.比如:用volatile修饰的变量,就会具有可见性.volatile修饰

  • 深度理解Java中volatile的内存语义

    volatile可见性实验 举个栗子 我这里开了两个线程,后面的线程去修改volatile变量,前面的线程不断获取volatile变量, 结果是会一致卡在死循环,控制台没有任何输出 假如将flag让volatile来进行修饰 结果是:三秒后,就不会不断打印出信息出来 注意,Thread.sleep是会刷新线程内存的,所以不要使用Thread.sleep来分别让一个线程获取两次volatile变量 volatile的特性 volatile其实相当于对变量的单词读或写操作加了锁.做了同步 由于是加了

  • Java中volatile关键字的作用

    目录 一.volatile作用 二.什么是可见性 三.什么是总线锁和缓存锁 四.什么是指令重排序 一.volatile作用 可以保证多线程环境下共享变量的可见性 通过增加内存屏障防止多个指令之间的重排序 二.什么是可见性 可见性是指当一个线程对于共享变量的修改,其他线程可以立刻看到修改之后的一个值,可见性本质上由几个方面造成的 cpu层面的高速缓存,在cpu里面呢设计了三级缓存去解决cpu运算效率和内存IO效率的问题,但是它也带来缓存一致性的一个问题,而在多线程执行的情况下呢,缓存一致性的问题就

  • java中volatile不能保证线程安全(实例讲解)

    今天打了打代码研究了一下java的volatile关键字到底能不能保证线程安全,经过实践,volatile是不能保证线程安全的,它只是保证了数据的可见性,不会再缓存,每个线程都是从主存中读到的数据,而不是从缓存中读取的数据,附上代码如下,当synchronized去掉的时候,每个线程的结果是乱的,加上的时候结果才是正确的. /** * * 类简要描述 * * <p> * 类详细描述 * </p> * * @author think * */ public class Volatil

  • Java中volatile关键字的作用与用法详解

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile 关键字作用是,使系统中所有线程对该关键字修饰的变量共享可见,可以禁止线程的工作内存对volatile修饰的变量进行缓存. volatile 2个使用场景: 1.可见性:Java提供了volatile关键字来保证可见性. 当一个共享变量被volatile修饰时,它会保证修

随机推荐