引言
在学习创建型模式中的单例模式的时候,我们都会接触到双重检测锁实现的单例模式(饱汉模式),代码如下:
ublic class Singleton {
/**
* 饱汉模式(双重检测锁)
*/
private Singleton(){};
private static volatile Singleton instance = null;
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}
可是为什么要使用volatile以及为什么要检测两次instance呢?
为什么使用Volatile
我们不妨先说一下volatile的作用
- 防止指令重排
- 指明被修饰的变量是不安全的,每次都必须到内存中读取
在单例模式中,我们使用volatile是为了防止指令重排,想象一下这种情况:当实例还没有创建的时候,线程A获得了synchronized锁,创建对象,虽然synchronized锁保证了操作的原子性,但是没有保证指令重排的正确性,如果构造函数中的操作很多,这个时候就可能出现instance已经被赋值了,但是还没有实例化完,这个时候如果线程B进入了,判断(instance==null)的时候就会误以为instance存在,直接返回,造成错误。
为什么要校验两次
想象一下这种情况:有两个线程同时通过了第一次判断instance==null,假设线程B获得了锁,此时线程A就处于等待状态。当线程B创建完实例之后,线程A可以获得这把锁,如果在这里没有第二次判断的话,线程A也会创建一个实例,这明显就与单例模式的初衷不符。
以上是我的个人理解。