单例模式

1.Singleton Pattern

单列指的是某一种类只有一个实例,而且自行实例化并向整个系统提供这个实例。有以下三个特点:

  • 自行实例化,该类的构造方法为private,外界对象无法通过new实例化。
  • 提供了全局的访问方法供整个系统所使用。
  • 整个系统中只会存在一个该类的实例。

1.1 饿汉式单例

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

当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。

1.2懒汉式单例

1
2
3
4
5
6
7
8
9
10
11
class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() { }
synchronized public static LazySingleton getInstance() { //使用synchronize确保多线程下的安全性
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}

懒汉式单例在第一次调用getInstance()方法时实例化,在类加载时并不自行实例化,这种技术又称为延迟加载(Lazy Load)技术,即需要的时候再加载实例。

  • 饿汉式单例类与懒汉式单例类比较:饿汉式单例在类被加载时就将自己实例化,故无需考虑多线程访问的问题,可以确保实例的唯一性;从调用速度和反应时间来说,由于单例对象从一开始就得以创建,因此要优于懒汉式单例。但是无论系统在运行时是否使用该单例对象,由于在类加载时该对象就需要创建,因此从资源利用效率角度,饿汉式不及懒汉式。而且系统加载时需要创建饿汉式单例对象,加载时间可能会比较长。懒汉式在第一次使用时创建,实现了延迟加载,但是必须处理好多线程访问的问题。无论是简单的加锁或是双重锁检查机制,都会对系统的性能产生一定影响。

1.3 双重检查锁定(Double-Check Locking,DCL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class LazySingleton {
private volatile static LazySingleton instance = null; //由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,因此即使使用双重检查锁定来实现单例模式也不是一种完美的实现方式。
private LazySingleton() { }
public static LazySingleton getInstance() {
//第一重判断
if (instance == null) {
//锁定代码块
synchronized (LazySingleton.class) {
//第二重判断
if (instance == null) {
instance = new LazySingleton(); // 创建单例实例
}
}
}
return instance;
}
}

进一步了解双重检查锁定与延迟初始化

1.4 一种更好的单例实现方法(Initialization Demand Holder,IoDH)

1
2
3
4
5
6
7
8
9
10
11
12
//Initialization on Demand Holder
class Singleton{
private Singleton(){ }
private static class HolderClass{
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return HolderClass.instance;
}
}