这是双重检查锁定的安全版本吗?

时间:2010-01-13 01:13:15

标签: multithreading language-agnostic concurrency locking thread-safety

这是一个我想出的一个想法,以便以安全,有效的方式处理Singleton同步问题。它基本上是双重检查锁定,但有一个涉及线程本地存储的扭曲。在Java / C#/ D样式伪代码中,假设__thread表示静态变量的线程局部存储:

class MySingleton {
    __thread static bool isInitialized; 
    static MySingleton instance; 

    static MySingleton getInstance() {
        if(!isInitialized) {
            synchronized {
                isInitialized = true;
                if(instance is null) {
                    instance = new MySingleton();
                }
            }
        }

        return instance;
   }
}

这保证只在程序的整个生命周期中每个线程输入一次synchronized块。从那时起,我们得到一个线程局部bool的简单检查,看看我们是否已经进入synchronized块并验证该对象是从该线程初始化的。

6 个答案:

答案 0 :(得分:4)

我不认为语言无关(或平台无关)的方法在这里是有用的,因为虽然构造可能在逻辑上是合理的,但是存在特定于实现的陷阱,这将阻止它正常工作。一个例子是双重检查锁定,它在Java pre-5上不起作用,因为它在JVM级别被破坏了。

因此,您应该在每个平台上使用可用的语言结构或库。

对于Java,您可以使用enum获取单身人士。

答案 1 :(得分:3)

这看起来很干净。实例对象的实际检查和初始化在同步块内,并且每个线程在第一次调用时被强制进入synchronized块,在线程之间得到一个干净的发生之前的边缘。

由于isInitialized是线程本地的,为什么要在synchronized块中设置它?此外,您应该只在构造单个对象后设置isInitalized。这样,如果它尚未初始化并且构造函数抛出,则该线程将在下次调用时再次检查。

    if(!isInitialized) {
        synchronized {
            if(instance is null) {
                instance = new MySingleton();
            }
        }
        isInitialized = true;
    }

答案 2 :(得分:2)

双重检查锁定被破坏的原因(据我所知)是instance不是空的可能性,但由于读/写重新排序而没有完全构造。

线程本地存储无法解决任何问题。它可能会让您不必将isInitialized声明为易失性,但仍然无法解决您的问题。

答案 3 :(得分:1)

是的,这个结构在我所知道的所有高级语言下都是安全的。特别是,在内存/并发模型保证给定线程始终按照与程序顺序一致的顺序(它几乎是任何有用的语言)看到它的自己的操作的任何语言都是安全的,并且其中synchronized块或等效块提供了关于块之前,之内和之后的操作的通常保证。

答案 4 :(得分:0)

就Java而言,对我来说很好看。如果您打算这样做,那么将线程本地存储作为参考是更常规的。读静电也没有意义。

但是在Java类加载是懒惰和线程安全的,所以你不妨写一下:

private static final MySingleton instance = new MySingleton(); 

或者根本不使用单身人士。

答案 5 :(得分:0)

是的,在新的jdk5 JMM下如果你声明实例 volatile DCL会起作用