并发4:懒汉模式与枚举模式
# 懒汉模式
# 懒汉模式(初始版)
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public Singleton getInstance() {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
return singleton;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
可以看到此时的懒汉模式的性能是非常差的, 每次将对它进行加锁后进行获取. 下面将对它进行优化
# 懒汉模式(加强版)
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public Singleton getInstance() {
if (singleton == null){
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在获取singleton
的时候,只有singleton
为空时才会对代码进行加锁,大大提高了程序的效率.
但是我们仔细看其中的创建对象的部分, 这个单例模式真的是线程安全的吗?
在JVM中Singleton singleton = new Singleton();
并不是一个原子性的操作,会执行以下三个步骤
- 给
singleton
分配空间、 - 调用
Singleton
的构造函数来初始化、 - 将
singleton
对象指向分配的内存空间,singleton
指向分配的内存空间后就不为null
了;
由于JVM的指令重排, 我们无法确保这三个步骤都在线程2在进行if (singleton == null)
的时候已经执行了2步骤
, 这时线程2获取到的singleton
对象就是一个空对象,如果调用就会引发空指针异常
# 懒汉模式(最终版)
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {
}
public Singleton getInstance() {
if (singleton == null){
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
volatile
关键字禁止了指令重排, 可以完美解决上方因指令重拍引发的问题
# 枚举模式
public class Singleton {
private Singleton() {
}
public static enum SingletonHandler {
SINGLETON;
private Singleton singleton;
SingletonHandler() {
singleton = new Singleton();
}
public Singleton getInstance() {
return singleton;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
上次更新: 2021/02/16, 15:47:09