(java基础)无双重判空的懒汉式单例为什么不能保证线程安全

懒汉式单例一般使用的时候有这么几个步骤

  • 私有化构造函数,防止使用者通过new创建
  • 对new对象的代码加锁,防止多个线程new对象
  • 在锁的上下各加一个判空
    这个创建步骤已经深入脑髓,所以一般是信手拈来.
    那么为什么不加判空的懒汉式单例不能保证线程安全?上代码

第一种写法

 public static DataEntityFactory getInstance() {
        //线程2等待
        synchronized (DataEntityFactory.class) {
            //线程1创建
            INSTANCE = new DataEntityFactory();
        }
        return INSTANCE;
    }

如果多线程获取单例,线程2被阻塞等待,线程1创建,线程1创建完成之后释放锁,那么线程2就又创建了一个对象,何谈单例?

那么一个判空能解决吗?

第二种写法

 public static DataEntityFactory getInstance() {
        //线程1,2分别到这里,往下走
        if (INSTANCE == null) {
            //线程2等待,线程1拿到锁,进入new对象阶段
            synchronized (DataEntityFactory.class) {
                    INSTANCE = new DataEntityFactory();
            }
            //线程2new对象完成,释放锁
        }
        return INSTANCE;
    }

代码注释很清楚了,当线程2释放锁之后,线程1已经进入到了第一道判空,那么剩下的情况就跟第一种写法一样了,无法保证单例

最终写法

public static DataEntityFactory getInstance() {
        //线程1,2走到这里,通过判空
        if (INSTANCE == null) {
            //线程2等待,线程1进入new对象
            synchronized (DataEntityFactory.class) {
                if (INSTANCE == null) {
                    INSTANCE = new DataEntityFactory();
                }
            }
            //线程1new对象结束,释放锁,线程2拿到锁
        }
        return INSTANCE;
    }

很显然,双重判空校验解决了这个问题,线程1释放完锁之后,线程2进入new对象阶段,但是通过判空,INSTANCE已经不为null,直接返回线程1创建的实例,完事儿!

有时候觉得自己是几年撸码的老程序员,就不关心这种非常细节基础的东西,其实程序健壮与否跟基础关系非常大!这也是大厂为什么那么注重基础,我虽身在小厂,但一直拥有一颗想去大厂的心

最后附上一个很完美的单例写法(jdk1.5支持枚举之后的写法,可以防止上述懒汉式单例通过反射创建多个对象的问题)
public enum SingleInstance {
    instance;

    private SingleInstance() {

    }

    public void testSignal() {
        //单例的具体方法
    }

    public void testSignal1() {
        //单例的具体方法
    }

    public void testSignal2() {
        //单例的具体方法
    }
}

你可能感兴趣的:((java基础)无双重判空的懒汉式单例为什么不能保证线程安全)