单例模式(Singleton Pattern)
单例模式属于创建型模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。
一个类有且仅有一个实例,并且自行实例化向整个系统提供。
角色:
1、 单例类(Singleton);
保证唯一并提供全局访问点的单例类。
命名空间SingletonPattern中包含7个单例类,本案例将介绍这7种常见的单例实现方法。
namespace SingletonPattern
public sealed class Singleton {
private static Singleton _instance = null;
public static Singleton GetInstance() {
if(_instance == null) {
_instance = new Singleton();
Console.WriteLine("Singleton.GetInstance()!");
}
return _instance;
}
}
最常见的单例类,但是无法保证线程安全。因为首次运行时,n个线程可同时到达if(_instance == null),导致_instance可能会被多初始化n-1次(有1次是需要初始化的)。在_instance被初始化之后新启动的线程不会使该情况重现。
public sealed class SingletonSafe {
private static SingletonSafe _instance = null;
private static readonly object _lock = new object();
public static SingletonSafe GetInstance() {
lock (_lock) {
if (_instance == null) {
_instance = new SingletonSafe();
Console.WriteLine("SingletonSafe.GetInstance()!");
}
}
return _instance;
}
}
使用私有静态object类型的锁(微软推荐),lock关键字会占有该锁,之后请求该锁的其它线程必需等待其释放才能进入。该方法可实现线程安全的单例模式,但是锁属于昂贵资源,“占有锁”和“释放锁”都比较耗时,并会在一定程度上阻止其它线程的执行,会显著影响程序的并发性,所以有了下面的优化。
public sealed class SingletonSafe2 {
private static SingletonSafe2 _instance = null;
private static readonly object _lock = new object();
public static SingletonSafe2 GetInstance() {
if (_instance == null) {
lock (_lock) {
if (_instance == null) {
_instance = new SingletonSafe2();
Console.WriteLine("SingletonSafe2.GetInstance()!");
}
}
}
return _instance;
}
}
通过优先使用if (_instance == null)这种耗费资源较少的比较来决定是否进入锁,可大幅度提高性能。因为_instance不为null时,直接返回即可。
public sealed class SingletonLazy {
private static readonly Lazy<SingletonLazy> _instance =
new Lazy<SingletonLazy>(() => {
Console.WriteLine("SingletonLazy.GetInstance()!");
return new SingletonLazy();
});
public static SingletonLazy GetInstance() {
return _instance.Value;
}
}
带泛型的Lazy式单例实现,这是线程安全的,仅提供给大家参考。
public sealed class SingletonReadOnly {
private static readonly SingletonReadOnly _instance =
new SingletonReadOnly();
public SingletonReadOnly() {
Console.WriteLine("SingletonReadOnly.GetInstance()!");
}
public static SingletonReadOnly GetInstance() {
return _instance;
}
}
静态只读式单例实现(由运行时保证唯一),这是线程安全的,仅提供给大家参考。
public abstract class SingletonGenericBase<T> where T : class, new() {
private static T _instance = null;
private static readonly object _lock = new object();
public static T GetInstance() {
if (_instance == null) {
lock (_lock) {
if (_instance == null) {
_instance = new T();
Console.WriteLine("SingletonGeneric.GetInstance()!");
}
}
}
return _instance;
}
}
public sealed class SingletonGeneric : SingletonGenericBase<Singleton> {
public SingletonGeneric() { }
}
复杂的泛型实现,这是线程安全的,仅提供给大家参考。
public abstract class SingletonGenericBase2<T> where T : class {
private static readonly Lazy<T> _instance = new Lazy<T>(() => {
var ctors = typeof(T).GetConstructors(
BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Public);
if (ctors.Count() != 1)
throw new InvalidOperationException(
String.Format("Type {0} must have exactly one constructor.",
typeof(T)));
var ctor = ctors.SingleOrDefault(
c => !c.GetParameters().Any() && c.IsPrivate);
if (ctor == null)
throw new InvalidOperationException(
String.Format("The constructor for {0} must be private and take no parameters.",
typeof(T)));
Console.WriteLine("SingletonGeneric2.GetInstance()!");
return (T)ctor.Invoke(null);
});
public static T GetInstance() {
return _instance.Value;
}
}
public sealed class SingletonGeneric2 : SingletonGenericBase2<SingletonGeneric2> {
private SingletonGeneric2() { }
}
复杂的泛型实现,这是线程安全的,仅提供给大家参考。
public class Program {
public static void Main(string[] args) {
var singleton = Singleton.GetInstance();
singleton = Singleton.GetInstance();
var singletonSafe = SingletonSafe.GetInstance();
singletonSafe = SingletonSafe.GetInstance();
var singletonSafe2 = SingletonSafe2.GetInstance();
singletonSafe2 = SingletonSafe2.GetInstance();
var singletonReadOnly = SingletonReadOnly.GetInstance();
singletonReadOnly = SingletonReadOnly.GetInstance();
var singletonLazy = SingletonLazy.GetInstance();
singletonLazy = SingletonLazy.GetInstance();
var singletonGeneric = SingletonGeneric.GetInstance();
singletonGeneric = SingletonGeneric.GetInstance();
var singletonGeneric2 = SingletonGeneric2.GetInstance();
singletonGeneric2 = SingletonGeneric2.GetInstance();
Console.ReadKey();
}
}
以上是调用方的代码,每个GetInstance方法均调用2次以展示效果。以下是这个案例的输出结果:
Singleton.GetInstance()!
SingletonSafe.GetInstance()!
SingletonSafe2.GetInstance()!
SingletonReadOnly.GetInstance()!
SingletonLazy.GetInstance()!
SingletonGeneric.GetInstance()!
SingletonGeneric2.GetInstance()!
优点:
1、 单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例;
2、 因为类控制了实例化过程,所以类可以灵活更改实例化过程;
缺点:
1、 没有接口,不能继承,与单一职责原则冲突;
使用场景:
1、 需要频繁的进行创建和销毁的对象;
2、 创建对象时耗时过多或耗费资源过多,但又经常用到的对象;
3、 工具类对象;
4、 频繁访问数据库或文件的对象;