设计模式系列-单例模式

懒汉模式

在第一次调用的时候实例化

双重检查锁模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 双重检查锁
*/
public class Singleton1 {

private volatile static Singleton1 singleton;

public static Singleton1 getInstance() {
if (null == singleton) {
synchronized (Singleton1.class) {
if (null == singleton) {
singleton = new Singleton1();
}
}
}
return singleton;
}

private Singleton1() {
// init some thing you want
}


}

注意点

  1. 这种写法在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例。
  2. 使用volatile保证正确性,保证这个实体只在主存中存在。

静态内部类模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 内部静态类
*/
public class Singleton2 {

private volatile static Singleton2 singleton;


private Singleton2() {
// init some thing you want
}

public static Singleton2 getInstance() {
return SingletonHolder.instance;
}


private static class SingletonHolder {
private static final Singleton2 instance = new Singleton2();
}

}

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

饿汉模式

饿汉式单例类:在类初始化时,已经自行实例化。

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 饿汉模式
*/
public class Singleton3 {

private final static Singleton3 singleton = new Singleton3();


private Singleton3() {
// init some thing you want
}

public static Singleton3 getInstance() {
return singleton;
}



}

实例直接使用final的模式,在构建后不可修改,又是static的,保证单例。

上述讲的几种单例模式实现中,有一种情况下他们会重新创建对象,那就是反序列化,将一个单例实例对象写到磁盘再读回来,从而获得了一个实例。反序列化操作提供了readResolve方法,这个方法可以让开发人员控制对象的反序列化。在上述的几个方法示例中如果要杜绝单例对象被反序列化是重新生成对象,就必须加入如下方法:

1
2
3
private Object readResolve() throws ObjectStreamException{
return singleton;
}

枚举模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 枚举模式
*/
public class Singleton4 {


enum Single {
SINGLE;

private Single() {

}

public void methodA() {
// do something
}

}


public static void main(String[] args) {
Single single = Single.SINGLE;
single.methodA();
}
}

创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量)。

枚举单例的优点就是简单,但是大部分应用开发很少用枚举,可读性并不是很高,不建议用