美文网首页
单例模式的实现方式

单例模式的实现方式

作者: hello_cc | 来源:发表于2016-01-14 17:29 被阅读48次

    最近看到组里有人实现单例模式,采用静态内部类的方式,不是很懂这种写法的优点,查了一下各种写法的优缺点,总结一下。
    内容多处参考文章:http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/

    懒汉式

    public class Single {
    
          private static Single mInstance;
          private Single(){}
    
          // 线程不安全
          public static Single getInstance() {
              if (mInstance == null) {
                  mInstance = new Single();
              }
              return mInstance;
          }
    
          // 线程安全,效率低,只有一个线程能调用getInstance()方法。
          public static synchronized Single getInstance() {
              if (mInstance == null) {
                  mInstance = new Single();
              }
              return mInstance;
          }
    
          // 同步代码块加锁,双重检查锁。
          public static Single getInstance() {
              if (mInstance == null) {  //Single Checked 
                  synchronized (Single.class) {
                      if (mInstance == null) { //Double Checked 
                          mInstance = new Single(); 
                      } 
                  } 
              } 
              return mInstance ;
          }
    }
    

    同步代码块加锁,双重检查

    • 这个是平时最常用的方式,看似完美,其实是有问题的。
      因为mInstance = new Single();这句语句的执行,不是一个原子操作,JVM在执行这条语句时,做了3个操作。
    1. 给mInstance分配内存。
    2. 调用Single的构造方法进行初始化。
    3. 将mInstance对象指向分配的内存空间(执行完这步mInstance就非空啦)。

    但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。

    饿汉式

    public class Single {
          // 类加载时就被初始化,线程安全
          private static final Single mInstance = new Single();
          private Single(){}
    
          public static Single getInstance() {
              return mInstance;
          }
    }
    

    缺点

    • 不是懒加载模式,类被加载时就被初始化。
    • 如果构造函数需要传递参数时,不能满足。

    静态内部类

    public class Single {
    
          private Single(){}
    
          private static class InnerHolder {
                private static final INSTANCE = new Single();
          }
    
          public static Single getInstance() {
                return InnerHolder.INSTANCE;
          }
    }
    

    这种写法仍然使用JVM本身机制保证了线程安全问题;由于 InnerHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

    总结

    单例模式最好采用静态内部类实现,但是如果对懒加载参数没有要求,饿汉式也可以。

    相关文章

      网友评论

          本文标题:单例模式的实现方式

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