美文网首页Android知识
Java DCL(Double Check Lock)单例模式详

Java DCL(Double Check Lock)单例模式详

作者: Go_SpursGo | 来源:发表于2017-08-24 10:24 被阅读0次

    单例模式是应用最广的模式之一。相信大家都非常熟悉了,什么,不熟悉?你都单例模式单刷了二十年了,还不懂?好吧,不懂的同学请自行百度。

    首先是最经典的痴汉...不是,饿汉模式:

    public class Girlfiriend extends Friend{
      private static final Girlfiriend  gf=new Girlfiriend ();
      private Girlfiriend (){
      }
      public static Girlfiriend getGirlfriend(){
            return gf;  
    }
    }
    

    饿汉模式什么都好,就是有点占地方,每次加载类的时候就存在了。有没有什么办法,用的时候在加载呢。没办法,世界是被懒人推动前进的,还真有。

    懒汉模式:

    public class Girlfiriend extends Friend{
      private static final Girlfiriend  gf;
      private Girlfiriend (){
      }
      public static synchronized Girlfiriend getGirlfriend(){
         if(gf==null){
            gf= new Girlfriend();
          }
            return gf;  
    }
    }
    

    这种方法实现了在需要的时候加载。但是注意这里的synchronized关键字,synchronized 保证了当进行多线程操作的时候不会产生多个实例。但是这种做法有一个缺点,不管是不是已经存在实例了,都会被锁阻塞。
    那么,有没有什么办法解决呢。这个世界是由聪明人改善,所以当然有!

    当当当当,DCL模式来了。DCL是不是听起来特别高大上,其实就是Double Check Lock,说人话就是,你瞅两次看看到底有没。

    public class Girlfiriend extends Friend{
      private static final Girlfiriend  gf;
      private Girlfiriend (){
      }
      public static  Girlfiriend getGirlfriend(){
         if(gf==null){
            synchronized(Girlfriend.class){
                if(gf==null){
                  gf= new Girlfriend();
                }
            }
          }
            return gf;  
    }
    }
    

    是不是感觉这就结束了。一篇好的文章是要出其不意的。所以,没完。
    DCL并不是十分稳定的,由于java编译器允许处理器乱序执行,所以这样做是有隐患的。
    简单说,其实new对象的操作不是原子性的。这句代码最终会被编译成多条汇编指令。
    (1)给Girlfriend的实例分配内存
    (2)调用Girlfriend()的构造函数,初始化成员字段
    (3)将gf对象指向分配的内存空间
    也就是说这三条指令顺序是不可预知的。当另一个线程执行第一个if(gf==null)的时候,由于已经调用了构造函数,但是构造还没有完成。会把没有构造完全的对象返回。
    那么怎么解决呢,聪明的你一定也能想到,只不过聪明人太多,有人先想到了:
    静态内部类单例模式:

    public class Girlfiriend extends Friend{
      private Girlfiriend (){
      }
      public static Girlfiriend getGirlfriend(){
            return GirlfriendMother.gf;  
    }
    private static class GirlfriendMother{
            private static final Girlfiriend gf= new Girlfiriend ();
    }
    }
    

    第一次加载Girlfriend的时候,gf不会被初始化,(java类只有被调用的时候才会初始化)。这种方式不仅能够确保线程的安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化。所以这才是推荐使用的单例模式实现的方式。

    嘿嘿,你以为到这就完了?too young too naive:
    最后是一种单例模式的终极形态——枚举单例:

    public enum Girlfriend{
    GIRLFRIEND;
    }
    

    默认枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例。

    相关文章

      网友评论

        本文标题:Java DCL(Double Check Lock)单例模式详

        本文链接:https://www.haomeiwen.com/subject/cqbjdxtx.html