单例模式

设计模式之单例模式

有一些对象我们只需要一个,如果创建了多个实例,有可能会出现各种问题,比如线程池、注册表、缓存等等,这个时候就要用到单例模式了。一开始我以为单例模式会很容易,不就是把构造方法设为私有再提供一个静态方法去获取这个实例嘛,然而仔细了解后我才发现事情并没那么简单,它居然有好几种写法……

事情没那么简单

1.定义

单例模式(Singleton Pattern)确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。

2.实例说明

2.1 饿汉模式

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

这种方式在类加载的时候直接完成初始化,因此类加载会有点慢,但是获取对象的速度快,而且可以避免多线程的同步问题。

2.2 懒汉模式(线程不安全)

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

先判断有没有实例,没有的话再创建实例。这种没有考虑到线程安全,多线程的时候可能会同时构造多个对象。

你可能想到了,我们在getinstance()方法前加锁同步不就得了嘛……所以就有了下面这种写法。

2.3懒汉模式(线程安全)

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;
}
}

这个时候每个线程进入到getinstance()方法之前都要等别的线程离开该方法,这得浪费多少资源啊……这种方式的效率是比较低的。

2.4 双重检查模式

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

这种模式对进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例。并且使用了volatile关键字,虽然牺牲了一点性能,但是提高了准确性。

2.5 静态内部类

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

第一次加载Singleton类时并不会初始化instance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder 并初始化instance ,这样不仅能确保线程安全也能保证Singleton类的唯一性,推荐使用静态内部类单例模式。

2.6 其它

除此之外还有枚举单例容器实现单例,不过我不是很熟,这里就不写了……等什么时候熟一点了我再写写吧!