设计模式-单例模式的各种实现

单例模式是开发中使用频率极高的一个模式,比如Spring的bean默认就是单例的。

概念

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

单例模式有一下特点:

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

实现方式

一般意义上来讲单例模式有两种实现方式,懒汉模式和饿汉模式。

饿汉模式

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

类加载自动初始化对象,不是lazy init ,系统启动的压力增大。

懒汉模式

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {  
private static Singleton instance;
private Singleton (){}

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

典型的懒汉模式,在需要初始化使用单例对象时才决定是否初始化,减轻了启动的负担,但是无法在多线程环境下运行,可能会导致产生多个实例。

改进版的懒汉模式

为了解决懒汉模式在多线程情况的问题,我们使用synchronized关键词同步代码,不就可以避免了同步问题了吗?

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

这种改良方式的确会解决多线程的问题,但同时synchronized会导致每次方法调用时锁住Singleton实例一次,性能消耗太大。而且instance一旦初始化后,同步锁更加拖累效率。

双重锁定的懒汉模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
2
3
4
5
6
7
8
9
public class Singleton {  
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

当getInstance方法第一次调用时,第一次读取SingletonHolder.INSTANCE,导致SingletonHolder类得到初始化,这种方式并没有使用任何线程同步的机制,就保证了一次访问域代码。延迟初始化没有增加访问成本,这个方法值得推荐。

结论

5种方式,相对比较推荐第4种和第5种,做到了延迟加载,也没有引入额外的问题和性能开销。