人生有涯,学海无涯

一、概述

单例模式(Singleton Pattern)是 Java 中最简略的设计模式之一。这种类型的设计模式属于创立型模式,它提供了一种创建对象的最佳形式。

这种模式波及到一个繁多的类,该类负责创立本人的对象,同时确保只有单个对象被创立。这个类提供了一种拜访其惟一的对象的形式,能够间接拜访,不须要实例化该类的对象。

留神:

  • 1、单例类只能有一个实例。
  • 2、单例类必须本人创立本人的惟一实例。
  • 3、单例类必须给所有其余对象提供这一实例。

二、七种实现

2.1、懒汉式 — 线程不平安

是否 Lazy 初始化:

是否多线程平安:

实现难度:

形容:这种形式是最根本的实现形式,这种实现最大的问题就是不反对多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种形式 lazy loading 很显著,不要求线程平安,在多线程不能失常工作。

public class Singleton {      private static Singleton instance;      private Singleton (){}        public static Singleton getInstance() {      if (instance == null) {          instance = new Singleton();      }          return instance;      }  }

2.2、懒汉式 — 线程平安

是否 Lazy 初始化:

是否多线程平安:

实现难度:

形容:这种形式具备很好的 lazy loading,可能在多线程中很好的工作,然而,效率很低,99% 状况下不须要同步。
长处:第一次调用才初始化,防止内存节约。
毛病:必须加锁 synchronized 能力保障单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很要害(该办法应用不太频繁)。

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

2.3、饿汉式

是否 Lazy 初始化:

是否多线程平安:

实现难度:

形容:这种形式比拟罕用,但容易产生垃圾对象。
长处:没有加锁,执行效率会进步。
毛病:类加载时就初始化,节约内存。
它基于 classloader 机制防止了多线程的同步问题,不过,instance 在类装载时就实例化,尽管导致类装载的起因有很多种,在单例模式中大多数都是调用 getInstance 办法, 然而也不能确定有其余的形式(或者其余的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的成果。

public class Singleton {      private static Singleton instance = new Singleton();      private Singleton (){}      public static Singleton getInstance() {          return instance;      }  }

2.4、饿汉式 — 变种

其实和之前的一样,仅仅是将单例的创立挪到了动态块。
public class Singleton {      private static Singleton instance;        static {        instance = new Singleton();    }        private Singleton (){}      public static Singleton getInstance() {          return instance;      }  }

2.5、枚举式

JDK 版本:JDK1.5 起

是否 Lazy 初始化:

是否多线程平安:

实现难度:

形容:这种实现形式还没有被宽泛采纳,但这是实现单例模式的最佳办法。它更简洁,主动反对序列化机制,相对避免屡次实例化。
这种形式是 Effective Java 作者 Josh Bloch 提倡的形式,它不仅能防止多线程同步问题,而且还主动反对序列化机制,避免反序列化从新创立新的对象,相对避免屡次实例化。不过,因为 JDK1.5 之后才退出 enum 个性,用这种形式写未免让人感觉陌生,在理论工作中,也很少用。
不能通过 reflection attack 来调用公有构造方法。

public enum Singleton {      INSTANCE;  }

2.6、双重校验锁式

JDK 版本:JDK1.5 起

是否 Lazy 初始化:

是否多线程平安:

实现难度:较简单

形容:这种形式采纳双锁机制,平安且在多线程状况下能放弃高性能。
getInstance() 的性能对应用程序很要害。

public class Singleton {      private volatile static Singleton singleton;      private Singleton (){}      public static Singleton getSingleton() {      if (singleton == null) {          synchronized (Singleton.class) {          if (singleton == null) {              singleton = new Singleton();          }          }      }      return singleton;      }  }

留神:

  1. volatile的应用,为了避免裸露一个未初始化的不残缺单例实例;
  2. 双重判空校验,第一个判断防止了频繁的加锁,第二个判断能够拦住多余的创立实例的线程;
  3. 加锁,保障了线程平安(只有一个实例)

这种实现形式是经常出现在面试题中的,而且常常会要求手写。

2.7、动态外部类

是否 Lazy 初始化:

是否多线程平安:

实现难度:个别

形容:这种形式能达到双检锁形式一样的效用,但实现更简略。对动态域应用提早初始化,应应用这种形式而不是双检锁形式。这种形式只实用于动态域的状况,双检锁形式可在实例域须要提早初始化时应用。

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

三、总结

下面列举的7种设计模式中第1种线程不平安,能够排除在外,第3、4种其实是一种,这样下来其实能够简化为5种形式:懒汉、饿汉、动态外部类、枚举、双重校验锁。