单例模式的实现方式

最近看到组里有人实现单例模式,采用静态内部类的方式,不是很懂这种写法的优点,查了一下各种写法的优缺点,总结一下。
内容多处参考文章: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 版本。

总结

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

你可能感兴趣的:(单例模式的实现方式)