单例模式的双重检查

时间:2020-07-12
本文章向大家介绍单例模式的双重检查,主要包括单例模式的双重检查使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

单例模式的双重检查

双重检查

public class Singletone{
    private static Instance instance;

    public Instance getInstance(){
        if(instance == null){
            synchronized(Singletone.class){
                if(instance == null){
                    instance = new Instance();
                }
            }
        }
        return instance;
    }
}

问题
instance = new Instance();是由三个步骤组成的:

  • 为对象分配内存
  • 实例化对象
  • 将引用指向对应的内存地址

但是第2,3步可能发生指令重排列,导致先将引用指向一个未实例化对象的内存地址,然后再进行实例化对象。
若此时第二个线程进行第一个非空判断时,则为false,会直接返回还没有实例化对象的内存地址,从而可能产生异常。

解决

  • 禁止指令重排列
  • 允许指令重排列,但是其他线程“不可见”

方案一:基于volatile禁止指令重排列

public class Singletone{
    private volatile static Instance instance;

    public Instance getInstance(){
        if(instance == null){
            synchronized(Singletone.class){
                if(instance == null){
                    instance = new Instance();
                }
            }
        }
        return instance;
    }
}

在成员变量中加入volatile变量,禁止使用new创建对象时的指令重排列。

方案二:基于类初始化的解决方案

public class Singletone{
    private static class InstanceHolder{
        public static Instance instance = new Instance();
    }

    public static Instance getInstance(){
        return InstanceHolder.instance;
    }
}

JVM在类初始化阶段进行类的初始化。在初始化期间,JVM会获取一个锁,从而同步多个线程对同一个类的初始化。

第一个获得锁的线程会完成实例对象的创建,其他线程会直接获取创建的实例对象。

参考:

$flag 上一页