普通的单例模式是线程不安全的,验证方法如下:
```
sealed class Singleton1
{
private Singleton1()
{
}
private static Singleton1 instance = null;
public static Singleton1 Instance
{
get
{
if (instance == null)
{
Console.WriteLine("Cup");
instance = new Singleton1();
}
return instance;
}
}
}
class Program
{
static void Main(string[] args)
{
Singleton1 st1 = null;
Singleton1 st2 = null;
while (true)
{
Thread t1 = new Thread(() =>
{
st1 = Singleton1.Instance;
});
Thread t2 = new Thread(() =>
{
st2 = Singleton1.Instance;
});
t1.Start();
t2.Start();
Thread.Sleep(100);
}
}
}
```
如上所示,打印的结果有很大概率出现两次"Cup",说明两个线程都创建了新的对象,单例被打破了。
改进方式:加锁
```
sealed class Singleton1
{
private Singleton1()
{
}
private static readonly object syncObj = new object();
private static Singleton1 instance = null;
public static Singleton1 Instance
{
get
{
lock (syncObj)
{
if (instance == null)
{
Console.WriteLine("Cup");
instance = new Singleton1();
}
}
return instance;
}
}
public void Clear()
{
instance = null;
}
}
```
运行结果只有一个"Cup",程序在进入代码段时首先判断有没有加锁,如果没有就加锁,另一个线程判断代码已经有锁了,就直接返回,从而保证了单例的唯一性。
缺点:判断锁状态和尝试加锁操作比较消耗性能
改进:锁前判断:
```
public static Singleton1 Instance
{
get
{
if (instance == null)
{
lock (syncObj)
{
if (instance == null)
{
Console.WriteLine("Cup");
instance = new Singleton1();
}
}
}
return instance;
}
}
```
如此,就只有第一次试图创建实例时要加锁
有没有不用锁的办法呢,也有:
```
sealed class Singleton1
{
private Singleton1()
{
}
private static Singleton1 instance = new Singleton1();
public static Singleton1 Instance
{
get
{
if (instance != null)
{
return instance;
}
else
{
return null;
}
}
}
}
```
C#在调用静态构造函数时初始化静态变量,运行时能够确保只调用一次静态构造函数。但这种机制不确定在其他语言中也存在
如果局限于C#中,还有更优化的方法:
```
sealed class Singleton1
{
private Singleton1()
{
}
public static Singleton1 Instance
{
get
{
return Nested.instance;
}
}
class Nested
{
static Nested()
{
}
internal static readonly Singleton1 instance = new Singleton1();
}
}
```
将创建实例的时机局限在获取Instance时。