单例双重锁的原因

package Single;

/**
 * 双检锁/双重校验锁(DCL,即 double-checked locking)
 *
 * JDK 版本:JDK1.5 起
 *
 * 是否 Lazy 初始化:是
 *
 * 是否多线程安全:是
 *
 * 实现难度:较复杂
 *
 * 描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
 * getInstance() 的性能对应用程序很关键。
 *
 */
public class SingleDoubleLock {
    private volatile static SingleDoubleLock singleton;
    private SingleDoubleLock (){}
    public static SingleDoubleLock getSingleton() {
        if (singleton == null) {
            synchronized (SingleDoubleLock.class) {
                if (singleton == null) {
                    singleton = new SingleDoubleLock();
                }
            }
        }
        return singleton;
    }
}

原因分析:
如果有两个线程同时到达,即同时调用getInstance() 方法,此时由于singleTon== null ,所以很明显,两个线程都可以通过第一重的 singleTon== null ,进入第一重 if语句后,由于存在锁机制,所以会有一个线程进入 lock 语句并进入第二重 singleTon== null ,另外的一个线程则会在lock 语句的外面等待。
而当第一个线程执行完new SingleTon()语句后,便会退出锁定区域,此时,第二个线程便可以进入lock 语句块,此时,如果没有第二重singleTon== null 的话,那么第二个线程还是可以调用 new SingleTon()语句,这样第二个线程也会创建一个SingleTon实例,这样也还是违背了单例模式的初衷的,所以第二重判断必须存在。
在没有第一重singleton == null 的情况下,也是可以实现单例模式的?那么为什么需要第一重 singleton == null呢?
都要判断是否为空,且判断是否为空之前,都要先加同步锁,如果线程很多的时候,就要先等待加了同步锁的线程运行完毕,才能继续判断余下的线程,这样就会造成大量线程的阻塞,且加锁是个非常消耗时间的过程,应该尽量避免(除非很有必要的时候)。

总结:
第一重锁作用:避免造成大量线程阻塞,避免性能消耗。
第二重锁作用:避免 singleTon== null时,第一个线程实例化后,进入阻塞状态的线程被唤醒后仍会进行实例化。
 

你可能感兴趣的:(单例)