懒汉模式
1. 最简单的懒汉模式
线程不安全,不推荐1
2
3
4
5
6
7
8
9
10
11
12
13public class Singleton {
private Singleton() {}
private static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
在多线程环境下,当多个线程执行到第9行代码时,此时instance都为空,那么这些线程都会执行第10行代码,从而创建了多个对象
2. 在上面的基础上给getInstance方法加上synchronized
线程安全,不推荐,加上synchronized之后,方法内的所有实现在同一时间只允许一个线程访问,可以保证线程安全,但是synchronized会带来性上很大的开销
1 | public class Singleton { |
3. 双重同步锁单例模式
线程不安全,可以改进
1 | public class Singleton { |
对于
instance = new Singleton();
这一行代码,实际上可以看出以下三步:
1、memory = allocate() 分配对象的内存空间
2、ctorInstance() 初始化对象
3、instance = memory 设置instance指向刚分配的内存
由于第二步和第三步不能由先行发生原则到处出来,JVM和cpu优化,发生了指令重排,顺序如下:
1、memory = allocate() 分配对象的内存空间
3、instance = memory 设置instance指向刚分配的内存
2、ctorInstance() 初始化对象
那么可能存在这样一种情况,在多线程情况下,当线程A执行到第10行代码instance = new Singleton();
的第3步instance = memory 设置instance指向刚分配的内存
时,线程B执行到第8行代码if (instance == null)
, 这时对象还没有初始化完毕,然而instance已经指向了分配给对象的内存,instance已经不为空,会造成对象逸出,因而线程不安全;
4. volatile + 双重检测机制,禁止指令重排
线程安全,推荐1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Singleton {
private Singleton() {}
private static volatile Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) { // 双重检测机制
synchronized (Singleton.class) { // 同步锁
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
饿汉模式,单例实例在类装载时进行创建
如果构造方法中没有很多的处理,那么饿汉模式是可以接收的,但是如果构造方法中有非常多的处理,会导致类加载的时候很慢,会导致一些性能的问题。 如果只进行类的加载,而没有进行实际的调用,就会造成资源的浪费,因此使用饿汉模式的时候应该考虑两个问题:
- 构造方法有没有过多的处理
- 这个类是否一定会被使用,避免装载之后没有调用造成资源浪费
1. 普通饿汉模式
线程安全,可以改进1
2
3
4
5
6
7
8
9
10public class Singleton {
private Singleton() {}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
2. 将创建对象的代码放到static块中
线程安全,推荐1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Singleton {
private Singleton() {}
private static Singleton instance = null;
static {
instance = new Singleton();
}
public static Singleton getInstance() {
return instance;
}
}
枚举模式
最安全,推荐1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class Singleton {
private Singleton() {}
public static Singleton getInstance() {
return Singleton.INSTANCE.getInstance();
}
private enum Singleton {
INSTANCE;
private Singleton singleton;
// JVM保证这个方法绝对只调用一次
Singleton() {
singleton = new Singleton();
}
public Singleton getInstance() {
return singleton;
}
}
}