之所以关注这个java内存模型的三大特性,源于看到java单例模式的最佳懒汉模式的写法:
// 双重校验锁DCL(double checked locking)
public class Singleton {
private static volatile Singleton sInstance;
private Singleton() {
}
public static Singleton getInstance() {
if (sInstance == null) {
synchronized (Singleton.class) {
if (sInstance == null) {
sInstance = new Singleton();
}
}
}
return sInstance;
}
}
如果没有用volatile修饰的话,这个获取到的单例实例是有问题的。因为
sInstance = new Singleton();
不是原子性的,它其实是一下三个步骤
- 1.为对象分配内存
- 2.执行构造方法语句,初始化实例对象
- 3.把sInstance的引用指向分配的内存空间
这三个步骤不一定是按顺序执行的,因为在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。所以可能执行的顺序是1-->3-->2
当执行完3时,这时候sInstance不为空,但是未进行初始化,这时候cpu切换执行权,另外一个线程在执行外层的
if(sInstance == null){
}
发现sInstance不为null,则直接return回来了,因为这个实例是未进行初始化的,所以是有问题的。
这是后就必须加上volatile,volatile能保证可见性和有序性,可以阻止
sInstance = new Singleton();
的重排序,按照正常1-->2-->3执行。而synchronized只能保证互斥性和原子性。这里可以参考下,大神的关于java内存模型的知识
网友评论