这是一个我想出的一个想法,以便以安全,有效的方式处理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块并验证该对象是从该线程初始化的。
答案 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会起作用