单例模式 双重检查判断 不一定安全
双重检查判断
public class Singleton {
private static Singleton instance=null;
private Singleton(){
}
public static Singleton getInstance() {
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
1)当线程A、B同时调用getInstance()方法,他们同时发现 instance == null成立(检查判断 1),同时去获取的Singleton.class锁
2)其中线程A获取到锁,线程B 处于等待状态;线程A会创建一个SingleTon实例,之后释 放锁
3)线程A释放锁后,线程B 被唤醒,线程B获取到锁,然后线程B检查
instance == null 不成立(检查判断2),不会再创建Singleton实例对象
编译优化后异常
我们认为的new Singleton()操作
- 1)分配内存地址 M
- 2)在内存 M 上初始化Singleton 对象
- 3)将M的地址赋值给 instance 对象
JVM编译优化后可能的new Singleton()操作
- 1)分配内存地址 M
- 2)将M的地址赋值给instance变量
- 3)在内存M上初始化 Singleton 对象
异常发生过程(如上图,JVM创建new Instance()对象时先赋值再初始化)
1)线程A先执行getInstance()方法,当线程A在执行完变量的内存地址赋值(尚未初始化)时,发生线程切换,线程B获得CPU的执行权
2)线程B在执行第一个判断,发现 instance == null条件不成立,直接返回instance,但此时instance并没有初始化,此时访问instance对象的成员变量就可能发生空指针异常
解决
上述问题出现的本质原因是,JVM在编译时的执行重排序造成的,所以只要禁止指令重排序,就可以解决这个问题,所以需要在Singleton对象的成员变量instance前加volatile关键字
private volatile static Singleton instance;