一、 单例(Singleton)模式
单例模式的特点:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其它对象提供这一实例。
单例模式应用:
- 每台计算机可以有若干个打印机,但只能有一个Printer Spooler,避免两个打印作业同时输出到打印机。
- 一个具有自动编号主键的表可以有多个用户同时使用,但数据库中只能有一个地方分配下一个主键编号。否则会出现主键重复。
单例模式三种写法:
第一种最简单,但没有考虑线程安全,在多线程时可能会出问题。
public class Singleton
{
private static Singleton instance = null;//定义一个实例对象
private Singleton(){}//构造函数
public static Singleton CreateInstance()//获得实例的方法
{
if(instance == null)//当前单例模式对象为空则创建
{
instance = new Singleton();
}
return instance;
}
}
第二种考虑了线程安全,经典写法
public class Singleton
{
private volatile static Singleton instance = null;//关于volatile: volatile多用于多线程的环境,当一个变量定义为volatile时,读取这个变量的值时候每次都是从内存里面读取而不是从缓存cache读。这样做是为了保证读取该变量的信息都是最新的,而无论其他线程如何更新这个变量。参考资料:官方文档
private static readonly object lockObj = new object();
private Singleton(){}
public static Singleton CreateInstance()
{
if(instance == null)
{
lock(lockObj )
{
if(instance == null)
instance = new Singleton();
}
}
return _instance;
}
}
第三种.NET Framework平台优势实现Singleton模式的代码
public class Singleton
{
private Singleton(){}
public static readonly Singleton instance = new Singleton();
}
非常吃惊是不是,不仅代码减少了许多,同时也解决了线程问题带来的性能上损失。有避免了编译器优化的问题。上述的代码真的能正常工作吗?如果能,那么它又是怎样工作的呢?
注意到,Singleton类被声明为sealed,以此保证它自己不会被继承,其次没有了Instance的方法,将原来instance成员变量变成public readonly,并在声明时被初始化。通过这些改变,我们确实得到了Singleton的模式,原因是在JIT的处理过程中,如果类中的static属性被任何方法使用时,.NET Framework将对这个属性进行初始化,于是在初始化Instance属性的同时Singleton类实例得以创建和装载。而私有的构造函数和readonly(只读)保证了Singleton不会被再次实例化,这正是Singleton设计模式的意图。
在什么情形下使用单例模式:
使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就不要使用单例模式。
注意:不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。