首页 > 代码库 > 设计模式之单例模式

设计模式之单例模式

1.前言
很多时候,我们需要为某个类型创建独一无二的对象。比如系统配置文件、工具类、线程池、缓存、系统日志等,此时单例模式应运而生。   
                单例模式: 确保一个类只有一个实例,并提供一个全局访问点
举例1
 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4  5 namespace 单例模式 6 { 7    public class Program 8     { 9         static void Main(string[] args)10         {11             Singleton s1 = Singleton.GetInstance();12             Singleton s2 = Singleton.GetInstance();13 14             if (s1 == s2)15             {16                 Console.WriteLine("Objects are the same instance");17             }18 19             Console.Read();20         }21     }22 23     public class Singleton24     {25         private static readonly Singleton instance = new Singleton();26         private Singleton() { }27         public static Singleton GetInstance()28         {29             return instance;30         }31     }32 }
从输出结果可知,两次实例化得到的是同一个对象。
 
 
2.单例模式的特点

单从实现来看,单例模式有以下特点
  • 构造函数访问权限为private,即不允许外界通过调用构造函数实例化;
  • 提供一个静态方法作为该类实例的唯一全局访问点
  • 任何时候只返回一个实例
单例模式UML类图如下
技术分享
 技术分享

 

技术分享
3.懒汉模式

如果在多线程下,能不能保证GetInstance()方法只创建一个实例呢?很显然是不行的。在C#中可以使用lock语句来保证对临界区进行完全访问(所谓的临界区是指在多线程访问共享资源时,使用独占式访问的代码段来对共享资源实施保护的一种手段)。接下来我们看看多线程下的单例模式
 
3.1举例2(多线程时的单例)
 1 namespace 单例模式 2 { 3     class Program 4     { 5         static void Main(string[] args) 6         { 7             Singleton s1 = Singleton.GetInstance(); 8             Singleton s2 = Singleton.GetInstance(); 9 10             if (s1 == s2)11             {12                 Console.WriteLine("Objects are the same instance");13             }14 15             Console.Read();16         }17     }18 19     public class Singleton20     {21         private static Singleton instance;22         private static readonly object syncRoot = new object();23         private Singleton()24         {25         }26 27         public static Singleton GetInstance()28         {29             lock (syncRoot)30             {31                 if (instance == null)32                 {33                     instance = new Singleton();34                 }35             }36             return instance;37         }38     }39 }

 这个例子能不能保证多线程安全呢?能。最先进入的那个线程会创建对象实例,保证类只实例化一次。但这样每次都lock的话,一旦多次调用时,第一个调用的会进入lock,而其他的线程则需要等待第一个结束才能依次调用,后面的依次调用,等待......所以会导致性能损耗。lock加锁就好比漏斗一样,每次只能样一个线程通过

技术分享

 

技术分享
3.2举例3(多线程下多重锁定的单例)
为了解决例子2中的多次锁定问题,我们进行稍微的改动,同时解决了线程安全和性能的问题。(若没有性能方面的顾虑,这个方法就是杀鸡用了牛刀)
 1 namespace 单例模式 2 { 3     class Program 4     { 5         static void Main(string[] args) 6         { 7             Singleton s1 = Singleton.GetInstance(); 8             Singleton s2 = Singleton.GetInstance(); 9 10             if (s1 == s2)11             {12                 Console.WriteLine("Objects are the same instance");13             }14 15             Console.Read();16         }17     }18 19     public class Singleton20     {21         private static Singleton instance;22         private static readonly object syncRoot = new object();23         private Singleton()24         {25         }26 27         public static Singleton GetInstance()28         {29             if (instance == null)30             {31                 lock (syncRoot)32                 {33                     if (instance == null)34                     {35                         instance = new Singleton();36                     }37                 }38             }39             return instance;40         }41     }42 }

 

4.饿汉模式
通过静态初始化的方式在类加载时就进行实例化操作,相比于懒汉模式,不存在线程安全的问题,如下
 1 namespace 单例模式 2 { 3     class Program 4     { 5         static void Main(string[] args) 6         { 7             Singleton s1 = Singleton.GetInstance(); 8             Singleton s2 = Singleton.GetInstance(); 9 10             if (s1 == s2)11             {12                 Console.WriteLine("Objects are the same instance");13             }14 15             Console.Read();16         }17     }18 19     public sealed class Singleton20     {21         private static readonly Singleton instance = new Singleton();22         private Singleton()23         {24         }25 26         public static Singleton GetInstance()27         {28             return instance;29         }30     }31 }

 

5.饿汉模式 VS 懒汉模式

饿汉模式:即利用静态初始化的方式,类一旦加载就立即实例化。其特点是加载类时比较慢,但运行时比较快,并且线程安全。
懒汉模式:即等到类第一次被引用时,才会对其实例化操作(延迟实例化 )。其特点是加载时比较快,但运行时比较慢,存在线程不安全的问题(需要双重锁定来保证多线程访问的安全性)。
 
今天是坚持晨读的第21天,也把这段时间的知识作下总结,写得不好的地方,欢迎讨论指出。

设计模式之单例模式