0
  • 最佳答案

    double check或许应该指的是下面这段代码


    public class Singleton {  
     private volatile static Singleton instance = null;  
     private Singleton() {}  
     public static Singleton getInstance() {  
      if (instance == null) {  
       synchronized (Singleton.class) {// 1  
        if (instance == null) {// 2  
         instance = new Singleton();// 3  
        }  
       }  
      }  
      return instance;  
     }  
    

    这种叫做双重检查模式(DCL)

    第一次是为了减少不必要的同步,提升性能

    第二次是在Singleton为空才创建实例 虽然volatile会耗费一点时间但是值得

    volatile的作用是避免程序出错 防止指令重排序带来的不良影响。比如

    a=1
    a=2
    a=3
    


    编译器就会省略前面 直接a=3

    一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。这个就是指令重排序。

    那我们那里可能会发生指令重排序呢,是在instance = new Singleton()这里。这里经过编译不是一个原子操作,他大概分为以下几步

    1. 分配内存
    2. 变量赋值
    3. 初始化对象

    当多线程来的时候可能一个线程正搞完第2步,他不为空了,还没搞第三步,另一个线程就来判断,嗯 不为空?直接修改,这是不对的。


    它在单线程,无并发没什么紧要,但是多线程就会有影响,加了这个就能确保程序的正确性

    它相比普通的线程安全的懒汉模式的好处是避免了不必要的同步等待的时间

    为什么他会省时间呢?

    1. 因为我们知道只有为空的时候才需要同步,才需要加锁和释放锁。所以第一个判断就是判断有没有被初始化,没有初始化再去获取锁,如果已经初始化就直接返回
    2. 第二次检查变量是否已经被初始化:如果其他线程曾获取过锁,那么变量已被初始化,返回初始化的变量。
    3. 如果第二次还没有被初始化 那就初始化并且返回变量

    它的缺点就是第一次加载反应会慢一些,还有就是可能会dcl失效。dcl失效我不太懂,但是查阅资料感觉还是和指令重排序或者指令的执行顺序有关。可以看看


    https://www.jianshu.com/p/c647fe58b98d


    静态内部类的方式创建单例创建单例的好处就是加载Singleton 类的时候不会初始化instance,只有调用getInstance()的时候才加载SingletonHolder并初始化instance。这样不仅能保证线程安全,也能保证Singleton类的唯一性。好处就是你用的时候才去占用内存,省空间。

    public class SingleTon{
      private SingleTon(){}
     
      private static class SingleTonHoler{
         private static SingleTon INSTANCE = new SingleTon();
     }
     
      public static SingleTon getInstance(){
        return SingleTonHoler.INSTANCE;
      }
    }
    

    下面是复制的 

    虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。

    故而,可以看出INSTANCE在创建过程中是线程安全的,所以说静态内部类形式的单例可保证线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

    =============================

    那么也就是说缺点就是可能造成进程阻塞。不过创建一个单例一般很快就好了,没啥影响。


    1195567795522260992  评论     打赏       ls666
    • ls666  回复 @ls666
      第一遍可能会有人有这样的问题dcl需要两个线程才能用吗?不是的如果是一个线程获得了锁并且为空的话就直接创建了,后面来的就直接返回结果了。应该是这样的吧
      ls666 2020-11-08 02:20   回复 1195567795522260992
相关问题
幻影~ · 安卓
2024-04-26 19:25 6 4
deanhu · AOSP
2024-04-25 21:53 3 10
幻影~ · 提问
2024-04-13 20:13 10 2
幻影~ · 找工作
2024-04-07 10:44 17 2
幻影~ · 问题
2024-03-31 17:20 7 2
TONYGFX · AOSP
2024-03-28 17:11 4 2