看破设计模式 2. 创建型模式 示例 疑问

Ensure a class has only one instance, and provide a global point of access to it.

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

示例

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HungrySingleton {

private static final HungrySingleton instance = new HungrySingleton();

private HungrySingleton() {
}

public static HungrySingleton instance() {
return instance;
}

public void say() {
System.out.println("hello");
}
}

懒汉式(加锁禁止重排序保证线程安全)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LazySingleton {

private static volatile LazySingleton instance = null;

private LazySingleton() {
}

public static LazySingleton instance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}

public void say() {
System.out.println("hello");
}
}

当然Effective java推荐枚举式

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
26
27
28
public class EnumSingleton {

private EnumSingleton() {
}

private enum Singleton {
INSTANCE;

private final EnumSingleton instance;

Singleton() {
instance = new EnumSingleton();
}

public EnumSingleton getInstance() {
return instance;
}
}

public static EnumSingleton instance() {
return Singleton.INSTANCE.getInstance();
}

public void say() {
System.out.println("hello");
}

}

我选择静态内部类式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class  {

private () {
}

private static class InstanceHolder {
private final static NiceSingleton instance = new NiceSingleton();
}

public static NiceSingleton instance() {
return InstanceHolder.instance;
}

public void say() {
System.out.println("hello");
}

}

疑问

  • 为什么懒汉式单例要双重校验?

当两个线程同时调用instance()方法时,由于singleton==null,两个线程都可以通过第一个校验,
然后线程A持有锁,线程B等待。当线程A执行完实例化、释放锁,线程B进入代码块。

如果不加第二个校验,线程B又会实例化一个对象。就会违反单例模式设计原则。

如果不加第一个校验,也能实现单例,但多个线程反复竞争锁会增加系统开销,严重影响性能。