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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 饿汉式,无论需不需要都直接创建
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() { }
public static Singleton getInstance() {
return singleton;
}
}

// 懒汉式,lazy-loading,需要的时候才创建
public class Singleton2 {
private static Singleton2 singleton2;
private Singleton2() { }
public static Singleton2 getInstance() {
if (singleton2 == null) {
singleton2 = new Singleton2();
}
return singleton2;
}
}

// 加锁,因为需要new的时候不多,大多数是读操作,所以效率不高
public class Singleton3 {
private static volatile Singleton3 singleton3;
private Singleton3() {}
public static Singleton3 getInstance() {
synchronized (Singleton3.class) {
if (singleton3 == null) {
singleton3 = new Singleton3();
}
return singleton3;
}
}
}

// 双重检测锁,在加锁之前检测是否是空,是空才加锁
public class Singleton4 {
private static volatile Singleton4 singleton4;
private Singleton4() {}
public static Singleton4 getInstance() {
if (singleton4 == null) {
synchronized (Singleton4.class) {
if (singleton4 == null) {
singleton4 = new Singleton4();
}
}
}
return singleton4;
}
}

// 静态内部类
public class Singleton5 {
private static class SingletonHolder {
private static final Singleton5 singleton5 = new Singleton5();
}
private Singleton5() {}
public static Singleton5 getInstance() {
return SingletonHolder.singleton5;
}
}

上面实现的缺点:

  • 都需要额外的工作(Serializable、transient、readResolve())来实现序列化,否则每次反序列化一个序列化的对象实例时都会创建一个新的实例。
  • 可能会有人使用反射强行调用我们的私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。

使用枚举除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。因此,《Effective Java》中推荐尽可能地使用枚举来实现单例。

1
2
3
4
5
6
// 枚举
public enum Singleton6 {
INSTANCE;
public void whateverMethod() {
}
}

总结,单例的三大要点:

  • 线程安全(未加锁的懒汉式和饿汉式线程不安全)
  • 延迟加载(只有饿汉式没有实现延迟加载)
  • 序列化与反序列化安全
谢谢小天使请我吃糖果
0%