Java 中的线程安全单例

维基百科上关于单例的文章提到了一些在Java中实现结构的线程安全方法。对于我的问题,让我们考虑具有冗长的初始化过程并且同时由许多线程访问的单例。

首先,这个未提及的方法是否线程安全,如果是这样,它同步什么?

public class Singleton {
    private Singleton instance;

    private Singleton() {
        //lots of initialization code
    }

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

其次,为什么以下实现线程在初始化时是安全的和懒惰的?如果两个线程同时进入该方法,究竟会发生什么情况?getInstance()

public class Singleton {
    private Singleton() {
        //lots of initialization code
    }

    private static class SingletonHolder { 
        public static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

最后,在第二个示例中,如果一个线程首先获取一个实例,另一个线程获取一个实例并尝试在第一个线程中构造函数完成之前对其执行操作,该怎么办?那么你能进入一个不安全的状态吗?


答案 1

答案1:方法使用类对象作为锁 - 即在这种情况下。static synchronizedSingleton.class

答案2:Java语言,其中包括:

  • 首次访问/使用类时加载类
  • 保证在允许访问类之前,所有静态初始值设定项都已完成

这两个事实意味着在调用 getInstance() 方法之前不会加载内部静态类。此时,在进行调用的线程被授予对它的访问权限之前,该类的静态实例将作为类加载的一部分进行实例化。SingletonHolder

这一切都意味着我们有安全的延迟加载,并且不需要任何同步/锁定!

此模式用于单例的模式。它击败了其他模式,因为它是单例的事实行业标准 - 每个使用它的人都知道他们正在处理单例(使用代码,很明显总是好的),所以这种模式在引擎盖下有正确的API正确的实现。MyClass.getInstance()

顺便说一句,比尔·皮尤(Bill Pugh)的文章值得一读,因为在理解单例模式时,它是完整的。


答案 2

推荐