1. 单例类只能有一个实例
  2. 单例类必须自己创建自己的唯一实例
  3. 单例类必须给其他对象提供这一实例

懒汉式

初始化时并不会直接创建实例,在使用时才会创建。

线程不安全方式

  1. public class Singleton{
  2. private static Singleton instance;
  3. private Singleton(){}
  4. public static Singleton getInstance(){
  5. if(instance == null){
  6. instance = new Singleton();
  7. }
  8. return instance;
  9. }
  10. }

线程安全方式

在方法上增加 synchronized 修饰。

  1. public class Singleton {
  2. private static Singleton instance;
  3. private Singleton(){}
  4. public static synchronized Singleton getInstance(){
  5. if(instance == null){
  6. instance = new Singleton();
  7. }
  8. return instance;
  9. }
  10. }

饿汉式 - 推荐

类加载时直接创建实例。

  1. public class Singleton {
  2. private static Singleton instance = new Singleton();
  3. private Singleton(){}
  4. private static Singleton getInstance(){
  5. return instance;
  6. }
  7. }

双重校验锁(DCL, Double Checked Locking) - 推荐

  1. public class Singleton{
  2. private static Singleton instance;
  3. private Singleton(){}
  4. private static Singleton getInstance(){
  5. if(instance == null){
  6. synchronized(Singleton.class){
  7. if(instance == null){
  8. instance = new Singleton();
  9. }
  10. }
  11. }
  12. return instance;
  13. }
  14. }

静态内部类/登记式

  1. public class Singleton{
  2. private Singleton(){}
  3. public static Singleton getInstance(){
  4. return SingletonInner.INSTANCE;
  5. }
  6. private static class SingletonInner{
  7. public static final Singleton INSTANCE = new Singleton();
  8. }
  9. }

枚举 - 推荐

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。

  1. public enum Singleton{
  2. INSTANCE;
  3. }

总结

一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。

参考