java - 使用双重检查阻塞方法实例化单例

标签 java concurrency singleton

我对这个实例化方法有疑问:

来自this网站上写着:

class Singleton
{
    private static Singleton instance;

    private Singleton()
    {
    System.out.println("Singleton(): Initializing Instance");
    }

    public static Singleton getInstance()
    {
        if (instance == null)
        {
            synchronized(Singleton.class)
            {
                if (instance == null)
                {
                    System.out.println("getInstance(): First time getInstance was invoked!");
                    instance = new Singleton();
                }
            }            
        }

        return instance;
    }

    public void doSomething()
    {
        System.out.println("doSomething(): Singleton does something!");
    }
}

然而,从 Elisabeth Freeman、Eric Freeman、Bert Bates、Kathy Sierra、Elisabeth Robson 所著的《Head First Design Pattern》一书中,他们使用相同的方法实例化 Singleton,唯一的区别是他们将私有(private)静态成员声明为 volatile 并且他们对声明它 volatile 做了很多修饰。为了在线程之间建立正确的“发生之前的关系”,声明“关键区域”同步还不够吗?

最佳答案

根据定义,使用 volatile 变量可以降低内存一致性错误的风险,因为对 volatile 变量的任何写入都会与同一变量的后续读取建立先发生关系。

问题在于,无序写入可能会导致在执行 Singleton 构造函数之前返回实例引用。

  1. 线程A注意到该值尚未初始化,因此它获得锁并开始初始化该值。

  2. 由于编程语言的语义,允许编译器生成的代码在 A 完成初始化之前更新共享变量以指向部分构造的对象。例如,在 Java 中,如果对构造函数的调用已内联,则一旦分配了存储空间,但在内联构造函数初始化对象之前,共享变量可能会立即更新。

  3. 线程 B 注意到共享变量已被初始化(或者看起来如此),并返回其值。因为线程 B 认为该值已经初始化,所以它不会获取锁。如果 B 在 A 完成的所有初始化被 B 看到之前使用该对象(因为 A 尚未完成初始化,或者因为对象中的某些初始化值尚未渗透到 B 使用的内存(缓存一致性)) ,程序可能会崩溃。

阅读此 wiki 以获得清晰的解释:http://en.wikipedia.org/wiki/Double_checked_locking_pattern#Usage_in_Java

关于java - 使用双重检查阻塞方法实例化单例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17001079/

相关文章:

android - 下载时我应该使用多少并发网络连接?

javascript - Electron:在主渲染器和渲染器中使用相同的单例

java - 我们可以为一个类只创建一个单一的对象吗?

python - 使用单例作为计数器

java - 有条件地将项目添加到 HashMap 的有效方法

java - 为自定义类实现 hashcode 和 equals

python - ZeroMQ worker 应该如何安全地 "hang up"?

scala - 如何在 `getOrElseComplete` 上执行 `Promise` ?

java - 荣格不能画白色矩形作为顶点

java - 如何: Write a File with FileOutputStream?