双重检查锁定代码如下:
public class Singleton{
// 静态属性,volatile保证可见性和禁止指令重排序
private volatile static Singleton instance = null;
// 私有化构造器
private Singleton(){}
public static Singleton getInstance(){
// 第一重检查锁定
if(instance==null){
// 同步锁定代码块
synchronized(Singleton.class){
// 第二重检查锁定
if(instance==null){
// 注意:非原子操作
instance=new Singleton();
}
}
}
return instance;
}
}
volatile作用:以下会涉及到Java内存模型的知识
禁止指令重排序:
new singleton()操作在Java中不是一个原子性的操作。这个动作大概分了3步
1.申请一个内存区域(空白内存)
2.调用构造方法等对singleton进行初始化(写内存)
3.将变量指针指向该对象内存区域(变量声明)
那么问题来了。虽然双重验证锁保证了进入锁时进行判断。但如果线程1在进行new操作时,由于指令重排序先执行了1、3而没有执行初始化的步骤。此时线程2进来判断instance非null直接返回了。那么线程2获得的是个不完整的对象,使用时就会报错。
保证可见性:
线程A在自己的工作线程内创建了实例,但此时还未同步到主存中;此时线程B在主存中判断instance还是null,那么线程B又将在自己的工作线程中创建一个实例,这样就创建了多个实例。
顺便提一下,volatile禁止指令重排序只能保证volatile修饰的代码之后的代码不会在它之前执行。