0

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

这个网站它说:

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,唯一的区别是他们将私有静态成员声明为volatile并且它们 rim关于声明它的要点相当多volatile。为了在线程之间建立正确的“发生之前的关系”,声明“临界区”同步是不够的吗?

4

1 回答 1

2

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

问题是乱序写入可能允许在执行 Singleton 构造函数之前返回实例引用。

  1. 线程A注意到该值没有被初始化,因此它获得了锁并开始初始化该值。

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

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

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

于 2013-06-08T15:43:04.660 回答