Design patterns 为什么这个单例构造代码不是线程安全的?
在这段代码中,为什么要写“非线程安全”? 多谢各位Design patterns 为什么这个单例构造代码不是线程安全的?,design-patterns,thread-safety,Design Patterns,Thread Safety,在这段代码中,为什么要写“非线程安全”? 多谢各位 class Singleton { private static Singleton _instance; // Constructor is 'protected' protected Singleton() { } public static Singleton Instance() { // Uses lazy initialization. // **Note: this is not t
class Singleton
{
private static Singleton _instance;
// Constructor is 'protected'
protected Singleton()
{
}
public static Singleton Instance()
{
// Uses lazy initialization.
// **Note: this is not thread safe.**
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
如果两个线程同时运行
If(_instance==null)
检查,而没有创建单例实例,那么它们都将尝试调用new
来创建单例实例,并将对它们的引用存储到同一个变量中
由于它们试图存储到的引用将在线程之间共享,因此此操作将不是线程安全的。另外,创建singletone类的两个实例可能会中断程序。因为Singleton不提供对_instance属性的互斥 使用锁来实现螺纹安全:
Object thisLock = new Object();
public static Singleton Instance()
{
lock (thisLock)
{
if (_instance == null)
{
_instance = new Singleton();
}
}
return _instance;
}
这个例子是C#-我不知道您使用的是哪种编程语言
因为在这种情况下可能会创建多个单例实例。假设两个线程已进入
实例
方法,并且尚未创建singleton对象(即\u实例
为空)。然后假设第一个线程执行if
条件并输入它。但是在它执行之前,会发生一个new
线程上下文切换,第二个线程开始执行。它还测试if
条件,发现该条件为空,并执行新建
。现在,第一个线程开始执行并创建该对象的另一个实例。基于RPM1984的回答:
我将以下内容用于锁定对象:
objectthislock=typeof(Sinlgeton)代码>
或者只是
...
lock( typeof( Singleton ) )
{
...
}
对于你们当中的表演大师:
public Singleton getInstance()
{
// the first query may save a performance-wise expensive lock - operation
if ( null == _instance )
{
lock ( typeof( Singleton ) )
{
if ( null == _instance )
{
_ instance = new Singleton()
}
}
}
return _instance;
}
顺便说一句:这被称为双锁单例模式。我认为在Java中,只需向getInstance方法添加一个synchronized标志就足够了。这将防止其他线程进入方法,而另一个线程在方法中 最大的问题是检查实例是否已经存在以及其延迟初始化不是原子操作
最简单的例子是线程A检查条件,然后让线程B;线程B检查相同的条件并创建一个新对象并返回该新对象;然后,线程B返回到线程A,线程A从它停止的地方重新开始;然后线程A去创建一个新对象并返回它
还有一些其他值得注意的问题:
应将_singleton变量标记为volatile,以确保正确发布写操作
类应标记为final或sealed,以防止子类出现问题
在Java中,应该重写clone()方法以引发UnsupportedOperationException
如果出于某种原因,需要使类可序列化,则还需要提供一个实现来处理该场景(否则,客户端可以连续反序列化流以创建多个实例)
这比我最初想象的更有趣:
所以最好的解决办法是
public sealed class Singleton
{
private static readonly Singletion _instance = new Singleton();
private Singleton()
{
//do your construction
}
public static Singleton getInstance()
{
return _instance;
}
}
从我目前的理解来看,编程环境(Java、.NET)对于这个解决方案来说并不重要
有什么想法或意见吗
进一步阅读,我发现:
- 单身人士MSDN:
- 静态成员上的MSDN:
特别是指
静态成员在
可以访问的静态成员是
第一次和静态测试之前
构造函数(如果有)是
打电话来
编辑:
至于Java,它也应该起作用:
- 但它不会偷懒,而是
Singleton将构造一次
类已加载
- 另见:第8.3.1.1节
- 看
现在如果有人指出一个保存C++版本,它就完成了…(我远离C++太长以至于不能记住细节…)
如果单独创建的成本很低,并且主要的要求是应用程序中的所有东西都看到相同的,则另一种方法是:
If TheSingleton Is Nothing Then
Dim newSingleton As New Singleton
If Interlocked.CompareExchange(TheSingleton, newSingleton, Nothing) IsNot Nothing Then
newSingleton.Dispose ' If applicable
End If
End If
如果辛格尔顿什么都不是
Dim newSingleton成为新的单身汉
如果互锁,则比较交换(Singleton、newSingleton、Nothing)不是Nothing
newSingleton.Dispose“如果适用
如果结束
如果结束
如果两个线程在其中一个执行CompareExchange之前通过了“If”测试,那么将创建两个单线程。然而,只有传递给CompareExchange的第一个单例将用于任何事情;另一个将被丢弃。实际上,给出的示例可能是类型安全的。此外,这甚至可能是一个合理的好主意
如果多个线程在第一个线程写入_实例之前(事实上,可能是在第一个线程写入_实例之后,但在第二个线程的CPU将新值加载到其缓存之前)执行null检查,则第二个线程(以及第三个线程和第四个线程…)线程将创建一个新实例并将其写入_实例
在垃圾收集语言中,这只是意味着在短时间内,多个线程将拥有自己版本的_实例,但很快它们将退出范围并符合垃圾收集的条件
现在,这是浪费,但它是否真的是一个问题取决于创建一个新实例的成本有多高,以及存在多个实例是否会产生任何负面后果。通常情况下,这种不必要的实例对象复制的负面影响很小,在这种情况下,负面影响可能小于锁定成本(锁定相对便宜但不是免费的,等待锁定可能非常昂贵,在某些情况下[死锁是最极端的情况]非常昂贵),甚至是CASsing。即使价格更高,也可能并不真正不安全
由于我们无法判断上述示例中是否存在这种情况,因此我们实际上不知道它是否是线程安全的
不过,最有可能的做法是在静态构造的基础上进行创建。语言?经验