大佬教程收集整理的这篇文章主要介绍了volatile 指令重排的理解,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
内存可见性只是 volatile
的其中一个语义,它还可以防止 JVM
进行指令重排优化。
举一个伪代码:
int a=10 ;//1
int b=20 ;//2
int c= a+b ;//3
一段特别简单的代码,理想情况下它的执行顺序是: 1>2>3
。但有可能经过 JVM 优化之后的执行顺序变为了 2>1>3
。
可以发现不管 JVM 怎么优化,前提都是保证单线程中最终结果不变的情况下进行的。
可能这里还看不出有什么问题,那看下一段伪代码:
private static Map<String,String> value ;
private static volatile Boolean flag = fasle ;
//以下方法发生在线程 A 中 初始化 Map
public void initMap(){
//耗时操作
value = getMapValue() ;//1
flag = true ;//2
}
//发生在线程 B中 等到 Map 初始化成功进行其他操作
public void doSomeThing(){
while(!flag){
sleep() ;
}
//dosomething
doSomeThing(value);
}
这里就能看出问题了,当 flag
没有被 volatile
修饰时, JVM
对 1 和 2 进行重排,导致 value
都还没有被初始化就有可能被线程 B 使用了。
所以加上 volatile
之后可以防止这样的重排优化,保证业务的正确性。
一个经典的使用场景就是双重懒加载的单例模式了:
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {
}
public static Singleton geTinstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
//防止指令重排
singleton = new Singleton();
}
}
}
return singleton;
}
}
这里的 volatile
关键字主要是为了防止指令重排。
如果不用 , singleton=newSingleton();
,这段代码其实是分为三步:
singleton
对象指向分配的内存地址。(3)加上 volatile
是为了让以上的三步操作顺序执行,反之有可能第二步在第三步之前被执行就有可能某个线程拿到的单例对象是还没有初始化的,以致于报错。
以上是大佬教程为你收集整理的volatile 指令重排的理解全部内容,希望文章能够帮你解决volatile 指令重排的理解所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。