java - 在Java中使用基于双重检查锁定的单例是否安全?

标签 java multithreading thread-safety final java-memory-model

Wikipedia 上列出了 Java 中 Singleton 的实现之一。 :

public class SingletonDemo {
    private static volatile SingletonDemo instance = null;

    private SingletonDemo() {
    }

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

Java Language Specification 17, paragraph 5指出

An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

好吧,假设我们的 SingletonDemo 类有非 Final 字段。那么,并发线程将能够读取默认值而不是构造函数中指定的正确值?

最佳答案

在 Java 5 及更高版本中可以正确实现双重检查锁定 (DCL)。在 Java 4 及更早版本中,这是不可能的,因为 volatile 在同步方面的行为没有正确指定(并且在实践中是不充分的)。

您问题中包含的代码是 DCL 的正确实现...当使用 Java 5 JRE 或更高版本运行时。

但是(IMO),不值得使用 DCL。特别是如果您(或您之后的开发人员)不完全理解如何正确/安全地做到这一点。

性能优势太小,在现实的 Java 应用程序中不值得进行优化。 (如果是的话,您可能过度使用/滥用单例......这会以其他方式困扰您!)

<小时/>

Ok, so imagine that our SingletonDemo class has non-final field. So, concurrent thread will be able to read default value instead of correct value specified in constructor?

(引用的 JLS 文本是关于完全不同的情况。它是关于 final 字段的。它与此处无关。并且您无法推断非 final 的行为> 具有同步的字段与不具有同步的 final 字段的行为。)

您的问题的答案是否定的。您问题中的代码足以保证并发线程不会看到默认值。要了解原因,请阅读以下内容:

  • JLS 第 17.4 条的全部内容,以及
  • Goetz 等人的《Java 并发实践》的最后一章,其中包含有关 DCL 的部分(如果我没记错的话……)

关于java - 在Java中使用基于双重检查锁定的单例是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21374188/

相关文章:

java - 如何仅获取最后的实际输出

java - BufferedWriter 适用于 Windows,但不适用于 Mac

java - 获取调用方法的注解

java - JPA - 使用多对多关系时,数据未保存在映射实体中

C++ Boost,在类中使用thread_group

c++ - 在某些东西本身变得线程安全之前,你要走多远?

c# - 如何在 C# 上使对象线程安全?

c# - 部分线程安全的字典

ios - 闭包不会在 swift 的主线程中运行

python - 同时输出到终端时保持用户输入的完整性