之前面试的时候被要求手写单例模式,还要对各种适用场景进行区分使用,虽然看了书,但是没做过总结,这次就记录下来吧。
单例模式
单例模式:保证一个类只有一个实例被创建,并且提供一个全局访问点。
代码实现
(1)
public sealed class Singleton
{
private static Singleton instance = null;
private Singleton() {}
public static Singleton CreateInstance()
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
上述方式确实可以实现单例模式的定义表达,但是一旦涉及到多线程的时候,就会出现问题。当多线程进行访问时,可能出现的情况就是创建出两个实例,所以上述这种情况是线程不安全的。
(2)
public sealed class Singleton
{
private static Singleton instance=null;
private static object _SingletonLock=new object();
private Singleton() {}
public static Singleton CreateInstance()
{
lock(_SingletonLock)
{
if (instance == null)
{
instance = new Singleton();
}
}
return instance;
}
第二种方式的代码通过创建一个锁就能解决多线程的问题了,一旦一个线程要实例化单例的时候,会对其加锁,其他线程如果要实例化的时候就会被阻塞于此,所以能成功实例化。但是又引出了新的问题,我们其实实例化单例后就不需要锁了,但是之后的每次访问都会遇到锁,这就造成了许多不必要的性能损耗。
(3)
public sealed class Singleton
{
private static Singleton instance=null;
private static object _SingletonLock=new object();
private Singleton() {}
public static Singleton CreateInstance()
{
if(instance==null)
{
lock(_SingletonLock)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
通过双if语句判断单例是否创建再加上一个锁就完美解决我们设想的各种情况了。
(4)当然,还有一种偷懒的方法
public sealed class Singleton
{
private static Singleton instance=new Singleton();
private Singleton() {}
public static Singleton CreateInstance()
{
return instance;
}
我们利用静态变量来实现单例模式,一旦程序运行,就实例化出了单例。但是缺点也很明显,如果我们暂时不需要访问这个全局单例,不就造成了浪费了么(提前实例化了)。
(5)使用Lazy
public sealed class Singleton
{
private static readonly Lazy<Singleton> instance = new Lazy<Singleton>(() => new Singleton());
private Singleton(){}
public static Singleton CreateInstance()
{
return instance.Value;
}
}
Lazy < T > 对象初始化默认是线程安全的,在多线程环境下,第一个访问 Lazy< T > 对象的 Value 属性的线程将初始化 Lazy< T > 对象,以后访问的线程都将使用第一次初始化的数据。