知识梳理--单例总结

作者: 三也视界 | 来源:发表于2017-11-14 00:39 被阅读22次

单例总结

0)Eager initialization

如果程序一开始就需要某个单例,并且创建这个单例并不那么费时

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
 }

这种实现方式有几个特点:

  • 实例一开始就被创建(Eager initialization)。
  • getInstance()方法不需要加synchronize来解决多线程同步的问题
  • final关键字确保了实例不可变,并且只会存在一个。

1)Lazy initialization

Volatile(不稳定的),这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。

Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

懒加载的方式使得单例会在第一次使用到时才会被创建.先看一种有隐患的写法:

public final class LazySingleton {
  private static volatile LazySingleton instance = null;

  // private constructor
  private LazySingleton() {
  }

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

请注意:上面的写法其实非线程安全的,假设两个Thread同时进入了getInstance方法,都判断到instance==null,然后会因为synchronized的原因,逐个执行,这样就得到了2个实例。解决这个问题,需要用到典型的double-check方式,如下:

public class LazySingleton {
    private static volatile LazySingleton instance = null;

    private LazySingleton() {       
    }

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

另外一个更简略直观的替代写法是:

public class LazySingleton {
    private static volatile LazySingleton instance = null;

    private LazySingleton() {       
    }

    public static synchronized LazySingleton getInstance() {
            if (instance == null) {
                instance = new LazySingleton ();
            }
        return instance;
    }
}

2)Static block initialization

如果我们对程序的加载顺序有点了解的话,会知道Static block的初始化是执行在加载类之后,Constructor被执行之前。

public class StaticBlockSingleton {
  private static final StaticBlockSingleton INSTANCE;

  static {
      try {
          INSTANCE = new StaticBlockSingleton();
      } catch (Exception e) {
          throw new RuntimeException("Error, You Know This, Haha!", e);
      }
  }

  public static StaticBlockSingleton getInstance() {
      return INSTANCE;
  }

  private StaticBlockSingleton() {
      // ...
  }
}

上面的写法有一个弊端,如果我们类有若干个static的变量,程序的初始化却只需要其中的1,2个的话,我们会做多余的static initialization。

3)Bill Pugh solution

University of Maryland Computer Science researcher Bill Pugh有写过一篇文章initialization on demand holder idiom

public class Singleton {
    // Private constructor prevents instantiation from other classes
    private Singleton() { }

    /**
    * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
    * or the first access to SingletonHolder.INSTANCE, not before.
    */
    private static class SingletonHolder {
        public static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

SingletonHolder类会在你需要的时候才会被初始化,而且它不影响Singleton类的其他static成员变量的使用。这个方法是线程安全的并且避免了使用volatile与synchronized。

4)Using Enum

这是最简便安全的方法。没有明显的缺点,并且避免了下面要讲到的序列化的隐患。

public enum Singleton {
    INSTANCE;
    public void execute (String arg) {
        // perform operation here 
    }
}

Serialize and de-serialize

在某些情况下,需要实现序列化的时候,普通的单例模式需要添加readResolve的方法,不然会出现异常。

public class DemoSingleton implements Serializable {
  private volatile static DemoSingleton instance = null;

  public static DemoSingleton getInstance() {
      if (instance == null) {
          instance = new DemoSingleton();
      }
      return instance;
  }

  protected Object readResolve() {
      return instance;
  }

  private int i = 10;

  public int getI() {
      return i;
  }

  public void setI(int i) {
      this.i = i;
  }
}

仅仅有上面的还不够,我们需要添加serialVersionUID,例子详见下面的总结。

Conclusion

实现一个功能完善,性能更佳,不存在序列化等问题的单例,建议使用下面两个方式之一:

Bill Pugh(Inner Holder)
public class DemoSingleton implements Serializable {
  private static final long serialVersionUID = 1L;

  private DemoSingleton() {
      // private constructor
  }

  private static class DemoSingletonHolder {
      public static final DemoSingleton INSTANCE = new DemoSingleton();
  }

  public static DemoSingleton getInstance() {
      return DemoSingletonHolder.INSTANCE;
  }

  protected Object readResolve() {
      return getInstance();
  }
}
Enum
public enum Singleton {
    INSTANCE;
    public void execute (String arg) {
        // perform operation here 
    }
}

相关文章

  • 知识梳理--单例总结

    单例总结 0)Eager initialization 如果程序一开始就需要某个单例,并且创建这个单例并不那么费时...

  • 单例模式

    单例模式是一种简单的的设计模式。虽然简单,但是应用却是十分的广泛。所有有必要针对单例模式的知识点进行梳理。 单例模...

  • 单例模式

    单例模式 最常用的单例模式,经常在项目中见,梳理后发现各种各样的单例,索性梳理一下。 自己创建自己,提供了访问其唯...

  • iOS基础知识梳理 - 单例模式

    一次性执行 还没有被执行的时候onceToken的值是0,如果已经被执行过了onceToken会变成非0 单例模式...

  • 单例模式

    懒汉单例 饿汉模式 总结

  • iOS 单例模式 or NSUserDefaults

    本文内容:iOS的单例模式NSUserDefaults的使用总结:iOS单例模式 and NSUserDefaul...

  • 【本人秃顶程序员】Java几种方式实现单例模式之经验总结

    ←←←←←←←←←←←← 快,点关注! 最近总结了几种单例模式的实现,做个小总结给大家分享一下。 1、 单例模式,...

  • 你真的会写单例?

    提到单例模式,相信都不会陌生,今天对其进行总结。 以下是单例模式的特点: 单例类只能有一个实例。 单例类必须自己自...

  • java单例模式(推荐)

    单例模式有许多写法,可以结合自己理解选择一个自己喜欢的 引入一个其他人的博客关于单例的不错总结; 单例 单例模式 ...

  • iOS主要知识总结--单例模式

    单例模式的实现 单例是一种很常见的模式,它的原理就是使程序运行过程中,某个类只有一个实例,无论以什么方式创建,都返...

网友评论

    本文标题:知识梳理--单例总结

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