1.保证变量对所有线程的可见性
当一个变量被声明为volatile,它会保证对这个变量的写操作立即刷新到主存当中,而对这个变量的读操作会直接从主存中读取,从而确保了多线程环境下对该变量访问的可见性。这意味着一个线程修改了被volatile修饰的变量的值,其他线程能立刻看到这个修改,不会受到各自线程工作内存的影响。
2.禁止指令重排
首先我们先了解一下指令重排,在执行程序时,为了提高性能,处理器和编译器常常会对指令进行重排序,不过指令重排是需要保证在单线程环境下不能改变程序运行的结果以及存在数据依赖关系的不允许重排序。我们熟知的单例模式中的双重检查锁就是为了避免指令重排的问题,在初始化单例对象时,由于编译器或cpu的指令重排,可能会导致另一个线程读取到未初始化完成的对象,这种情况就可以使用volatile关键字来避免。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // 可能会发生指令重排
}
}
}
return instance;
}
}
instance = new Singleton()实际上分为三个步骤
1.分配内存空间
2.初始化对象
3.将对象指向内存地址
如果指令重排的话,顺序可能就变为132,接着当正在执行2的时候,另一个线程来读取这个对象,读取到的就是一个未初始化完成的对象