提到设计模式,首先要提到的就是单例模式。单例模式是设计模式中最简单最常用的一种模式,其思路就是:确保一个类只有一个实例,并且提供一个全局访问点。
为什么会需要单例模式呢?因为开发过程中经常会碰到一个数量有限的资源,比如说打印机,任务管理器等等,在对这些资源进行开发设计的过程中自然而然就衍生出了单例这种模式。
下面我就列举一下大家常用的代码实现。
首先是最简单经典的饿汉模式,代码如下:
/// <summary>
/// 饿汉单例模式,无论是否调用,都会占用系统资源
/// </summary>
public class HungrySingleton
{
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton()
{
}
public static HungrySingleton getInstance()
{
return instance;
}
}
此方法简单好记,但有一个明显的缺点就是,不管用不用得上此单例,系统都会初始化并占用资源,基于这个问题,就衍生出了懒汉模式。
/// <summary>
/// 懒汉单例模式,使用时才会创建单例,但是不适用高并发多线程
/// </summary>
public class LazySingleton
{
private static LazySingleton instance;
private LazySingleton() { }
public static LazySingleton getInstance()
{
if (instance == null)
{
instance = new LazySingleton();
}
return instance;
}
}
懒汉模式很好地解决了资源占用问题,但是在多线程的环境下,容易产生多个实例,于是在懒汉模式的基础上,又出现了锁定单例模式。
/// <summary>
/// 锁定单例模式,保证了唯一性,但是每次活动单例都要独占,有性能瓶颈
/// </summary>
public class LockSingleton
{
private static LockSingleton instance;
private static readonly object locker = new object();
private LockSingleton() { }
public static LockSingleton getInstance()
{
lock (locker)
{
if (instance == null)
{
instance = new LockSingleton();
}
}
return instance;
}
}
锁定单例模式解决了懒汉模式的并发问题,但是在高并发的环境下,有性能瓶颈,所有的单例请求都要被lock一次。对于这个问题,目前大家常用的解决方案是,再加一把锁,也就是双重锁定单例模式。
/// <summary>
/// 双重锁单例模式,绝大多数情况下,能保证唯一性
/// </summary>
public class DoublelockSingleton
{
private static DoublelockSingleton instance;
private static readonly object locker = new object();
private DoublelockSingleton() { }
public static DoublelockSingleton getInstance()
{
if (instance == null)
{
lock (locker)
{
if (instance == null)
{
instance = new DoublelockSingleton();
}
}
}
return instance;
}
}
除此之外,还有一个比较不错的方案,就是内部静态类单例模式,同样也可以实现多线程高并发环境下的延迟加载。其使用的原理就是,静态成员是在类首次被使用时完成实例化过程。
/// <summary>
/// 静态内部类单例模式,线程安全
/// </summary>
public class StaticSingleton
{
private class InnerInstance
{
/// <summary>
/// 当一个类有静态构造函数时,它的静态成员变量不会被beforefieldinit修饰
/// 就会确保在被引用的时候才会实例化,而不是程序启动的时候实例化
/// </summary>
static InnerInstance() { }
internal static StaticSingleton instance = new StaticSingleton();
}
private StaticSingleton()
{
}
public static StaticSingleton getInstance()
{
return InnerInstance.instance;
}
}
看到这里可能还会有读者不明白,静态内部类和饿汉模式比起来,到底有什么不同。关于这个问题,我在上一篇文章《C# 静态内部类单例模式-静态变量何时初始化》中有详细分析,感兴趣的可以点击进入查看。
总结
介绍了这么多中玩法,我希望大家(特别是初学者)能对单例模式有一个更加深入的理解。