为什么我们需要在获取锁之前和之后检查null? 一次,我们获得了锁,没有线程可以拥有该锁,那么为什么在同步块之前不需要进行null检查呢?
public class DclSingleton {
private static volatile DclSingleton instance;
public static DclSingleton getInstance() {
**if (instance == null) {**
synchronized (DclSingleton .class) {
**if (instance == null) {**
instance = new DclSingleton();
}
}
}
return instance;
}
// private constructor and other methods...
}
答案 0 :(得分:5)
想象下一个场景:
instance == null
并发现此条件为真。instance == null
并发现此条件为真。instance = new DclSingleton()
。instance = new DclSingleton()
。 我们有两次初始化。答案 1 :(得分:2)
您两次检查null
是因为:
DclSingleton.class
上进行同步之前进行检查,则每个呼叫都将被同步,这可能会很慢(可以想象经常使用单例实例)。null
内的synchronized
块,则有可能多个线程进行了第一次检查而又没有机会锁定对象,然后您将重新创建实例。答案 2 :(得分:0)
要了解为什么需要进行两次null
检查,请查看已经给出的答案。
安全初始化单例实例的另一种方法是静态持有人模式,其实现方式如下:
public class DclSingleton {
public static DclSingleton getInstance() {
return Holder.INSTANCE;
}
private static class Holder {
private static final DclSingleton INSTANCE = new DclSingleton();
}
}
JVM已以线程安全的方式初始化类,因此,即使2个线程同时访问getInstance()
,JVM也会仅初始化Holder
类一次,因此您可以进行正确的初始化。
此外,Holder
类将延迟加载,因此仅在首次引用时才将其初始化。例如,getInstance()
是第一次被调用