DCL实现代码
private volatile static Singleton INSTANCE;
public static Singleton getInstance(){
if (INSTANCE == null){
synchronized (Singleton.class){
if (INSTANCE == null){
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
三个问题
- 为什么外层要判空
外层加判空的目的是为了避免每次获取实例的时候都需要获取锁和释放锁,这样会带来很大的性能消耗,外层判空可以在已经初始化完成后,直接返回实例对象。
- 为什么要内层判空
内层判空是为了保证对象的单例,因为在多线程情况下,如果没有内层判空的话,那么多个线程可能在竞争锁之前都已经通过了外层判空逻辑,那么在这种情况下,会出现多个实例对象。所以加上内层判空,那么另一个线程进来后,再次判空的时候对象已经被之前释放锁的线程初始化完成,那么自然不会进入new对象的逻辑中,从而保证了对象的单一。
- 为什么变量需要使用volatile
主要用例禁止指令重排序。因为INSTANCE = new Singleton() 主要分为三步:1.为对象开辟内存空间 2.对象初始化 3.将对象引用赋值个变量。 如果能够保证2,3的顺序那么就不会存在安全问题,但是实际因为JIT和处理器会对代码进行优化重排序,那么可能会2,3的顺序颠倒,那么就有可能会出现一个线程拿到了一个未被初始完成的对象,从而引发安全问题。所以需要添加volatile关键字禁止指令重排序