单例模式主要特点不是根据客户程序调用生成一个新的实例,而是控制某个类型的实例数量唯一一个。即单例模式就是保证在整个应用程式的生命周期中,在任何时刻,被知道的类只有一个实例,并为客户程序提供一个获取该实例的全景访问点
一.经典模式
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { public class Instance { //经典模式 private static Instance instance; private Instance() { } public static Instance GetInstance() { if (instance == null) { instance = new Instance(); } return instance; } } }
解释:在经典模式下,没有考虑多线程并发获取实例的问题,即有可能出现两个线程同时获取实例,且此时为null时,会出现两个线程创建了两个实例,违反了单例模式的规则。
二.多线程下的单例模式
1.Lazy(懒汉) 模式
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { public class Instance { //Lazy(懒汉)模式 private static Instance instance; private static object _lock = new object(); private Instance() { } public static Instance GetInstance() { if (instance == null) { lock (_lock) { if (instance == null) { instance = new Instance(); } } } return instance; } } }
解释:上述代码使用双重锁定方式较好的解决了多线程下的单例模式实现,先看内层的if语句块,使用这个语句块时,先进行加锁操作,保证只有一个线程可以访问该语句块,进而保证只创建了一个实例。再看外层的if语句块,这使得 每个线程欲获取实例时不必每次都得加锁,因为只有实例为空时(即需要创建一个实例),才需加锁创建,若果已存在一个实例,就直接返回该实例,节省了性能开 销。两次判断Instance==null是第二个是为了万一一个线程刚锁定时,已经有一个线程实例了一个Instance,此时如果不判断会又实例一个
2.饿汉模式(特点:自己主动实例)
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { public class Instance { private static readonly Instance instance = new Instance(); private Instance() { } public static Instance GetInstance() { return instance; } } }
解释:上面使用的readonly关键可以跟static一起使用,用于指定该常量是类别级的,它的初始化交由静态构造函数实现,并可以在运行时编译。在这种模 式下,无需自己解决线程安全性问题,CLR会给我们解决。由此可以看到这个类被加载时,会自动实例化这个类,而不用在第一次调用 GetInstance()后才实例化出唯一的单例对象。
不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。