在软件系统中,经常有这样一些特殊的类,必须保证他们在系统中只存在一个实例,才能确保它们的逻辑正确性以及良好的效率 这应该是类设计者的责任,而不是使用者的责任。
有两种模式可以实现单例:
- 懒汉式(需要调用的时候才创建实例)
public class Singleton
{
/// <summary>
/// 构造函数耗时耗资源
/// </summary>
private Singleton()
{
Thread.Sleep(1000);
Console.WriteLine("{0}被构造一次", this.GetType().Name);
}
private static volatile Singleton _Singleton = null;
private static readonly object Singleton_Lock = new object();
public static Singleton CreateInstance()
{
if (_Singleton == null) //保证对象初始化之后,不在去等待锁
{
lock (Singleton_Lock) //保证只有一个线程进去
{
if (_Singleton == null)//保证只被实例化一次
_Singleton = new Singleton();
}
}
return _Singleton;
}
public int total;
public void do(){
total++;
}
}
- 饿汉式(一开始就创建实例)
public class Singleton
{
/// <summary>
/// 构造函数耗时耗资源
/// </summary>
private Singleton()
{
Thread.Sleep(1000);
Console.WriteLine("{0}被构造一次", this.GetType().Name);
}
/// <summary>
/// 静态构造函数:由CLR保证,程序第一次使用这个类型前被调用,且只调用一次
/// </summary>
static Singleton()
{
_Singleton = new Singleton();
Console.WriteLine("Singleton 被启动");
}
/// <summary>
/// 静态字段:在第一次使用这个类之前,由CLR保证,初始化且只初始化一次
/// </summary>
//private static Singleton _Singleton = new Singleton(); //可以使用静态字段替换静态构造函数
private static Singleton _Singleton = null;
public static Singleton CreateInstance()
{
return _Singleton;
}
public int total;
public void do(){
total++;
}
}
要点:
- 一般不要支持ICloneable接口,因为这可能导致多个对象实例
- 一般不要支持序列化,因为这也有可能导致多个对象实例
- 使用单例模式不能解决线程安全问题,如果多线程访问其中的字段,需要加lock保证线程安全。例如上面代码中,如果有1000个线程共同调用do方法,则最终total的值会小于1000
- 泛型不能作为单例,因为泛型类型里面的静态字段,是随着不同的类型参数唯一的