首页 > 代码库 > 单例模式

单例模式

什么是单例模式?

      简单来说就是要创建一个独一无二的对象

      确保一个类只有一个实例,并提供一个全局的访问点。

有哪些使用场景?

  •  线程池
  •  缓存
  •  注册表
  •  充当打印机、显卡等设备的驱动程序的对象

 剖析经典的单件模式的实现 

技术分享
 1 public class Singleton 2 { 3         private static Singleton singleton; 4         private Singleton() 5         {        6         } 7         public static Singleton GetInstance() 8         { 9             if (null == singleton)10             {11                 singleton = new Singleton ();12             }13             return singleton;14         }15  }
View Code

 

1.利用静态变量来记录唯一的实例
2.把构造器声明为私有的,只有singleton类内部才可以调用构造器
3.用getInstance()实例化对象,并返回这个实例。
4.null==singleton是空的表示没有创建实例,如果它不存在我们就可以利用私有构造器产生一个singleton的实例并把它赋值到静态变量中。请注意如果我们不需要这个实例,他就永远不会产生。这就是“延迟实例化”
5.如果不是null,就说明已经有了实例,我们直接跳到return语句。

 

 单件模式的类图

 

技术分享

 单件模式的序列图

 

 

 

技术分享

 Client调用

技术分享
 1  public class Client 2     { 3         static void Main(string[] args) 4         { 5             Singleton s1 = Singleton .GetInstance(); 6  7             Singleton s2 = Singleton .GetInstance(); 8  9             Console.WriteLine(s1 == s2);10 11             Console.ReadKey();12         }13     }
View Code

 输出结果: True

 技术分享

client调用结果分析
1.从上面的结果可以看出尽管两次访问了GetInstance(),但是访问的只是同一个实例。
2.由于构造函数设为私有的,类外部无法使用new实例化一个实例,只能通过getInstance()创建一个实例,如果存在则不会创建实例,而是调用以前生成的实例。

 

分析经典单例模式存在的问题

如果多个线程同时调用呢?我们举个例子,例如有线程A和B两个线程,A线程进入if判断语句后还没实例化,B线程到达,此时singleton还是为null,这样的话就让两个线程均通过if语句判断,然后调用new singleton()了,这样就创建了两个实例,很显然就违反了单例模式的初衷了。

 

如何解决多线程的问题?

方法一:双重检查锁定

技术分享
 1 public class Singleton  2     {  3         private static Singleton singleton; 4         private static readonly object syncObject = new object(); 5        private Singleton()  { } 6         public static Singleton GetInstance()  7         {  8             if (singleton == null)  9             { 10                 lock (syncObject) 11                 {     12                     if (singleton == null) 13                     { 14                         singleton = new Singleton(); 15                     } 16                 } 17             } 18             return singleton; 19         } 20     } 
View Code

 

1、例如有两个线程A和B,当线程A到达lock下面的if语句时,线程B也到到达lock语句,此时线程B不会往下执行,因为已经锁定,需要等线程B执行完lock语句块,才能往下走。

2、可以看到上面的代码有两个if(singleton==null),第一个其实是基于性能的考虑,如果没有第一个if(single==null),那每次调用这段代码时,当有多个线程时都会造成线程阻塞。而加了以后,如果已经创建了对象,当调用到第一个if(single==null)时,就会跳到return语句,不会执行lock代码。保证了性能最优。

3、第二个if(singleton==null)保证了多线程的环境下只创建一个对象。

其中需要注意lock的用法

1.lock关键字的参数必须为引用类型,如果是值类型,会报编译错误。
2.锁定的类型必须是安全的,所以是private的,如果是public的话可能其他地方也会锁定它,可能会导致死锁。

方法二:饿汉式单例

技术分享
 1 namespace Singleton {   2        public  class Singleton   3        {  4            private static readonly Singleton singleton = new Singleton(); 5            private Singleton()  {  } 6            public static Singleton GetInstance()  7            {   8                return singleton;  9             } 10         } 11     }
View Code

 

1、当程序一启动时就创建了一个对象,并用静态变量来接收它。

2、当调用GetInstance()方法时直接返回静态变量。

3、在整个程序中只有一次创建对象,就是程序启动时。所以在多线程使用中不会产生问题。

4、在程序一启动时就已经创建了对象,也就提前占用了资源,当对提前占用资源不关心的话,可以使用此方法。

 

总结:

  • 单例模式使类在程序生命周期的任何时刻都只有一个实例
  • 单例的构造函数是私有的
  • 必须通过 GetInstance()来请求得到这个单例类的实例。

 

单例模式