单例模式

摘要

在我们日常的工作中经常需要在应用程序中保持一个唯一的实例,如:IO处理,数据库操作等,由于这些对象都要占用重要的系统资源,所以我们必须限制这些实例的创建或始终使用一个公用的实例,这就是我们今天要介绍的——单例模式(Singleton)。

 

定义

 单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点

 

结构图

结构图分析

包含角色:Singleton(唯一)

私有构造函数:确保用户无法通过new来直接实例它

静态私有成员变量instance:用于存放实例化的对象

Instance():检验并实例化自己,然后存放在静态成员变量中,确保实例的唯一性

 

几种常见的单例模式

1.线程不安全

2.线程安全

3.Double-Checked Locking

4.静态初始化

5.延迟初始化

6.Net4’s Lazy<T> type

 

线程不安全版

 

 1 public sealed class SimpleSingleton : BaseSingleton
 2     {
 3         private static SimpleSingleton _instance = null;
 4         private SimpleSingleton() { }
 5         public static SimpleSingleton Instance
 6         {
 7             get
 8             {
 9                 return _instance ?? (_instance = new SimpleSingleton());
10             }
11         }
12     }
View Code

单线程环境,因为在多线程的环境下有可能得到Singleton类的多个实例。假如同时有两个线程去判断(null == _singleton),并且得到的结果为真,那么两个线程都会创建类Singleton的实例,这样就违背了Singleton模式“唯一实例”的初衷。使用条件:

改进方案:线程安全模式,支持多线程

 

线程安全

 1 public class ThreadSafeSingleton : BaseSingleton
 2       {
 3          private static ThreadSafeSingleton _instance = null;
 4          private static readonly object SynObject = new object();
 5          private ThreadSafeSingleton() { }
 6          public static ThreadSafeSingleton Instance {
 7              get {
 8                  lock (SynObject) {return _instance ?? (_instance = new ThreadSafeSingleton());}
 9              }
10          }
11      }
View Code

首先我们创建了一个静态只读的进程辅助对象SynObject,由于lock是确保当一个线程位于代码的临界区时,另一个线程不能进入临界区(同步操作)。分析:

如果其他线程试图进入锁定的代码,则它将一直等待,直到该对象被释放。从而确保在多线程下不会创建多个对象实例了。

缺点:这种实现方式要进行同步操作,影响系统性能的瓶颈和增加了额外的开销。

改进方案:经典的Double-Checked Locking,降低同步操作的次数

 

Double-Checked Locking

 

 1 public sealed class DoubleCheckedLockingSingleton : BaseSingleton
 2     {
 3         private static DoubleCheckedLockingSingleton _instance = null;
 4         private static readonly object SyObject = new object();
 5         private DoubleCheckedLockingSingleton() { }
 6         public static DoubleCheckedLockingSingleton Instance {
 7             get {
 8                 if (null != _instance) { return _instance; }
 9                 lock (SyObject) {return _instance ?? (_instance = new DoubleCheckedLockingSingleton());}
10             }
11         }
12     }
View Code

这种模式支持了多线程的环境,也降低了同步操作的次数,降低了系统的开销。这就是经典的Double-CheckedLocking方法。分析

下面,讨论其他其中单例模式的实现方案。

其中涉及到的一个新的概念beforefieldinit,意思大概就是:该字段初始化可以发生在任何时候任何字段被引用之前。这里就不做过多介绍,暂且跳过。

 

静态初始化

 

 1 public sealed class ThreadSafeNoLockSingleton : BaseSingleton
 2     {
 3         private static readonly ThreadSafeNoLockSingleton _instance = new ThreadSafeNoLockSingleton();
 4         static ThreadSafeNoLockSingleton() { }
 5         private ThreadSafeNoLockSingleton() { }
 6         public static ThreadSafeNoLockSingleton Instance
 7         {
 8             get { return _instance; }
 9         }
10     }
View Code

优点:较之前的实现方式更为简单,且支持多线程环境分析

由于这种静态初始化的方式是在自己的字段被引用时才会实例化。因此即便很多线程试图引用_instance,也需要等静态构造函数执行完并把静态成员_instance实例化之后可以使用。单例的实现由C#语言本身的特性来。

 

延迟初始化

 

 1 public sealed class LazySingleton : BaseSingleton
 2     {
 3         private LazySingleton() { }
 4         public static LazySingleton Instance { get { return Nested._instance; } }
 5  
 6         private class Nested
 7         {
 8             static Nested() { }
 9             internal static readonly LazySingleton _instance = new LazySingleton();
10         }
11     }
View Code

这种方案把初始化工作放到内部类Nested类中的一个静态成员来完成,以这种方式实现了延迟初始化分析

Lazy<T> type

 

1 public sealed class DotNetLazySingleton : BaseSingleton
2     {
3         private static readonly Lazy<DotNetLazySingleton> lazy = new Lazy<DotNetLazySingleton>(() => new DotNetLazySingleton());
4         private DotNetLazySingleton() { }
5         public static DotNetLazySingleton Instance { get { return lazy.Value; } }
6     }
View Code

这种方式简单、性能良好,而且还提供检查是否已经创建实例的属性IsValueCreated.这是.Net4提供的一种解决方案分析

演示:

源代码:https://git.oschina.net/xrainchen/FrameworkDemo.git 分支:1.15.11.22.1

总结

单例模式(Singleton)会控制其实例对象的数量,从而确保访问对象的唯一性。

实例控制:单例模式防止其它对象对自己的实例化,确保所有的对象都访问一个实例。

伸缩性:因为由类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。

参考:http://csharpindepth.com/Articles/General/Singleton.aspx

李宏:

单例最典型例子,就是反转依赖IOC里面最常用到的,单例最大好处在于他只有一个实例,每一次实例化都要有一次开销,如果某个方法常调用,那么他就有成千上万次实例化,开销是很大的
单例最大缺点,他因为只有一个实例,在高并发的情况下,如果有共享资源,导致线程不安全,锁这种办法是会影响性能

根据李宏的点评,我找了一篇文章:http://www.cnblogs.com/atwanli/articles/4740184.html

 

posted @ 2015-11-24 14:07  AntColony  阅读(130)  评论(0)    收藏  举报