为什么在双重检查锁定中使用易失性

Head First 设计模式手册中,具有双重检查锁定的单例模式已实现如下:

public class Singleton {
    private volatile static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

我不明白为什么被使用。使用方法是否违背了使用双重检查锁定(即性能)的目的?volatilevolatile


答案 1

理解为什么需要的一个很好的资源来自JCIP的书。维基百科对该材料也有一个体面的解释volatile

真正的问题是,在完成构造之前,可能会分配一个内存空间。 将看到该分配并尝试使用它。这会导致失败,因为它使用的 是 的部分构造版本。Thread AinstanceinstanceThread BThread Binstance


答案 2

正如@irreputable所引用的那样,波动性并不昂贵。即使它很昂贵,一致性也应该优先于性能。

对于懒惰的单例来说,还有一种干净优雅的方式。

public final class Singleton {
    private Singleton() {}
    public static Singleton getInstance() {
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

来源文章 : 来自维基百科的demand_holder_idiom初始化

在软件工程中,按需初始化持有者(设计模式)习语是一个延迟加载的单例。在所有版本的Java中,该成语支持安全,高度并发的惰性初始化,并具有良好的性能

由于该类没有任何要初始化的变量,因此初始化会轻松完成。static

在 JVM 确定必须执行 LazyHolder 之前,不会初始化其中的静态类定义。LazyHolder

静态类仅在类 Singleton 上调用静态方法时执行,第一次发生这种情况时,JVM 将加载并初始化该类。LazyHoldergetInstanceLazyHolder

该解决方案是线程安全的,不需要特殊的语言构造(即 或 )。volatilesynchronized


推荐