Singleton用双重检查阻塞方法实例化

时间:2013-06-08 15:33:59

标签: 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,唯一的区别是他们将私有静态成员声明为volatile他们认为volatile声明了这一点。为了在线程之间建立正确的“幸福关系”,不应该宣布“临界区”同步吗?

1 个答案:

答案 0 :(得分:2)

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

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

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

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

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

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