禁止重排序、防止代码重排序是volatile关键字的作用之一
什么是指令重排序
public void set() {
int a=1;
int b=2;
}
CPU为了提供程序执行效率,会对指令执行顺序进行重排,以上代码,b赋值可能先于a赋值执行。
但以下代码不会涉及指令重排,因为b赋值依赖于a。
public void set() {
int a=1;
int b=a;
}
指令重排序所带来的问题
对于单线程来说,指令重排会提高CPU执行效率,基本上没有任何问题,但对于多线程,重排会导致指令执行的不确定性。
举个例子,下面是经典的懒汉单例模式
public class Singleton {
public static Singleton singleton;
private Singleton() {};
public static Singleton getInstance() {
if (singleton == null) {
synchronized (singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
分析:
先要了解对象的构造过程,实例化一个对象其实可以分为三个步骤:
- 分配内存空间。
- 初始化对象。
- 将内存空间的地址赋值给对应的引用
但是由于操作系统可以对指令进行重排序,所以上面的过程也可能会变成如下过程:
- 分配内存空间。
- 将内存空间的地址赋值给对应的引用。
- 初始化对象
如果是这个流程,多线程环境下就可能将一个未初始化的对象引用暴露出来,从而导致不可预料的结果。因此,为了防止这个过程的重排序,我们需要将变量设置为volatile类型的变量。
网友评论