美文网首页
Java volatile如何防止指令重排序

Java volatile如何防止指令重排序

作者: 鹅鹅鹅_ | 来源:发表于2019-01-24 20:11 被阅读0次
voliate关键字的两个作用

1、 保证变量的可见性:当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷新到主内存中。当一个线程用到被volatile关键字修饰的值的时候,虚拟机会强制要求它从主内存中读取。
2、 屏蔽指令重排序:指令重排序是编译器和处理器为了高效对程序进行优化的手段,它只能保证程序执行的结果时正确的,但是无法保证程序的操作顺序与代码顺序一致。这在单线程中不会构成问题,但是在多线程中就会出现问题。非常经典的例子是在单例方法中同时对字段加入voliate,就是为了防止指令重排序。

编译期重排序的典型就是通过调整指令顺序,做到在不改变程序语义的前提下,尽可能减少寄存器的读取、存储次数,充分复用寄存器的存储值
比如我们有如下代码:

int x = 10;
int y = 9;
x = x+10;

假设编译器直接对上面代码进行编译,不进行重排序的话,我们简单分析一下执行这段代码的过程,首先加载x变量的内存地址到地址寄存器,然后会加载10到数据寄存器,然后CPU通过mov指令把10写入到地址寄存器中指定的内存地址中。然后加载y变量的内存地址到地址寄存器,加载9到数据寄存器,把9写入到内存地址中。进行第三行执行时,我们发现CPU需要重新加载x的内存地址和数据到寄存器,但如果我把第三行和第二行换一下顺序,那么执行过程中对于寄存器的存取就可以少很多次,同时对于程序结果没有任何影响。

另一个例子可以看下面的双重检查锁构造单例的代码

public class Singleton {
    private volatile static Singleton singleton;

    private Singleton() {}

    public static Singleton getInstance() {
        if (singleton == null) { // 1
            synchronized(Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton(); // 2
                }
            }
        }
        return singleton;
    }
} 

实际上当程序执行到2处的时候,如果我们没有使用volatile关键字修饰变量singleton,就可能会造成错误。这是因为使用new关键字初始化一个对象的过程并不是一个原子的操作,它分成下面三个步骤进行:

a. 给 singleton 分配内存
b. 调用 Singleton 的构造函数来初始化成员变量
c. 将 singleton 对象指向分配的内存空间(执行完这步 singleton 就为非 null 了)

如果虚拟机存在指令重排序优化,则步骤b和c的顺序是无法确定的。如果A线程率先进入同步代码块并先执行了c而没有执行b,此时因为singleton已经非null。这时候线程B到了1处,判断singleton非null并将其返回使用,因为此时Singleton实际上还未初始化,自然就会出错。synchronized可以解决内存可见性,但是不能解决重排序问题。

但是特别注意在jdk 1.5以前的版本使用了volatile的双检锁还是有问题的。其原因是Java 5以前的JMM(Java 内存模型)是存在缺陷的,即时将变量声明成volatile也不能完全避免重排序,主要是volatile变量前后的代码仍然存在重排序问题。这个volatile屏蔽重排序的问题在jdk 1.5 (JSR-133)中才得以修复,这时候jdk对volatile增强了语义,对volatile对象都会加入读写的内存屏障,以此来保证可见性,这时候2-3就变成了代码序而不会被CPU重排,所以在这之后才可以放心使用volatile。

相关文章

  • Java volatile如何防止指令重排序

    voliate关键字的两个作用 1、 保证变量的可见性:当一个被volatile关键字修饰的变量被一个线程修改的时...

  • volatile 关键字

    volatile 关键字的作用:就是保证变量的可见性,防止指令重排序。 在 JDK1.2 之前,Java 的内存模...

  • volatile 关键字

    volatile 关键字的作用:就是保证变量的可见性,防止指令重排序。 在 JDK1.2 之前,Java 的内存模...

  • 单例模式注意事项

    单例中的类变量定义加 volatile : 禁止java指令重排序

  • java并发机制的底层实现原理

    1、volatile定义与实现原理volatile修饰的共享变量能够保证可见性以及防止指令的重排序,并且能够保证对...

  • 双重检查锁定(double-checked locking)与单

    可以使用volatile变量禁止指令重排序,让DCL生效: [java]view plaincopy packag...

  • volatile

    目标 1、volatile如何保证内存可见性2、volatile如何禁止指令重排序3、内存屏障4、内存可见性5、关...

  • 多线程之volatile

    保证可见性,总结:volatile作用1、可以保证可见性、防止内存指令重排序2、lock(汇编) ->缓存锁(me...

  • Java指令重排序与volatile关键字

    Java指令重排序与volatile关键字 1. 重现代码重排序 1.1 测试代码 完整代码参见Github,其中...

  • volatile类型变量

    volatile是java虚拟机提供的最轻量级的同步机制。 volatile的两种特性:可见性和禁止指令重排序优化...

网友评论

      本文标题:Java volatile如何防止指令重排序

      本文链接:https://www.haomeiwen.com/subject/ejiujqtx.html