c#单例模式

一、概述

       设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。而设计模式中最常见的大概就是单例模式了,关于单例模式的文章有很多,有的已经讲得非常好了。本文试图从3W的角度来说明单例模式。

二、主要内容

1.为什么需要单例模式 (why)

        单例模式的存在是理所当然的。比如中国有很多皇帝,但是同一时期只能有一个皇帝,那个时期的百姓一谈皇帝就知道是说的哪一个皇帝。又比如你是一个富翁,你有很多汽车,但是某一时刻你只能从车库里拿出一辆车坐在里面,不可能坐在两辆车里,除非你有分身术。再比如画画程序的工具箱,注册表编辑器,打印程序等。用专业的话来说,就是某一个时刻,一个类只能有一个实例化的对象

2.什么是单例模式(what)

          单例模式的定义如下:“ensure a class has only one instance,and provide a global point of access to it.”(确保一个类只有一个实例,并提供一个访问它的全局访问点)
       关于这句话,有以下几点需要说明:
     (1)单例类有且只有一个实例。说到这里,我们需要提一下静态类了。静态类是不能实例化的(任何地方都不能),它只有静态的属性和方法。静态类主要用于共享和提供通用的功能,例如提供数学计算的math类。单例模式中的类不能在类之外实例化,但是可以在类里面实例化。这也正是它为什么能够提供一个实例的原因。
     (2)既然不允许外部new,那么需要将构造函数私有化
     (3)外部要能访问这个实例化的对象。很简单,我们只需要提供一个静态方法,供外部访问该实例即可。
       然后我再来看类图就十分轻松了:

类图

3.如何使用单例模式(how)

通过单例模式的定义及分析,写出单例模式的类就很容易了。
  public sealed class Singleton{
            private static readonly Singleton singleton = new Singleton ();//内部实例化一个对象
            private Singleton (){}//将构造函数私有化,防止外部创建对象
            public static Singleton getInstance(){return singleton;}//提供一个访问点
       } 
        我们来分析这段代码,sealed关键字是防止类派生,因为派生也可能产生出新的实例。readonly关键字表明字段只能作为声明的一部分或者构造函数中出现。当整个类被加载的时候,就会自行初始化 singleton 这个静态只读变量。也就是说,不管我们用没用到,它就已经开始存在了,而不是我们需要的时候才去实例化一个对象出来。这种方式称之为:饿汉单例模式
       如果我们再懒一点,想在需要它的时候再实例化呢?这个不难实现,只需要稍微做一下改动就好了:

public class Singleton{
            private static Singleton singleton;
            private Singleton (){}//将构造函数私有化,防止外部创建对象
            public static Singleton getInstance()//提供一个访问点
             {
               if(singleton==null)//若实例不存在则创建实例,否则返回该实例
                 {
                  singleton = new Singleton()
                 }
               return singleton;
            }
       } 

       这个在需要时才创建实例的方式称之为:懒汉单例模式。似乎问题解决了,但是这个是线程不安全的。在单线程下,这个没有任何问题,在多线程下面就可能会出现多个实例!原因就在于singleton==null的判断。例如线程A执行到singleton==null,发现没有实例,于是开始创建新实例,在还没创建完成的时候,线程B恰好也执行到singleton==null,也发现没有实例,于是也开始创建新实例,这样就会出现两个实例!这能叫单例模式吗?显然不能!

       怎么解决呢?其实,我们可以通过给正在创建实例的线程加上一把锁,这样别的线程就无法创建新的实例了。关于lock的使用可以参考MSDN,地址如下:http://msdn.microsoft.com/zh-cn/library/c5kehkcz(VS.80).aspx

            正确的饿汉单例模式:

public class Singleton 
    { 
        private static Singleton singleton;
        private static readonly object syncObject = new object();
        private Singleton()  { }
        public static Singleton GetInstance() 
        { 
            if (singleton == null) 
            { 
                lock (syncObject) 
                {
                    if (singleton == null) 
                    { 
                        singleton = new Singleton(); 
                    } 
                } 
            } 
            return singleton; 
        } 
    } 

关于这段代码,有以下几点需要说明:

1.类中有定义了一个私有静态的只读对象  syncObject。
        lock 关键字的参数必须为基于引用类型的对象,该对象用来定义锁的范围,singleton 最初为 null ,是无法实现加锁的,所以必须要一个实例化的对象即 syncObject 来定义加锁的范围。
2.在 GetInstance()中,在 if 语句中使用两次判断 singleton == null ,这叫做双重检查锁定。
           先解释第二个singleton == null 的必要性,它是为了防止出现多个实例而设置的。假设线程A顺利到达第二个singleton == null,发现没有实例,于是乎开始创建实例。创建实例的时候线程B通过第一个singleton == null的判断,顺利到达lock,发现被锁住了,于是B被阻塞。当A创建实例完成后,解锁,此时B阻塞解除顺利进入,如果没有第二个singleton == null的判断,那么B就要开始创建实例了,那么就会同时存在两个实例!所以正确的做法是,线程B再次判断singleton == null,发现有实例了,于是直接返回该实例即可。
     再来解释第一个singleton == null。没有第一个singleton == null的判断,程序其实是正常的,通过分析就可以知道。而问题在于,如果没有这个“多余”的判断,在多线程下,每个线程到这里都会执行加锁的操作,这是不必要的,而且是耗费性能的。

          饿汉单例和懒汉单例的使用,要根据实际情况。不过引用《大话设计模式》中的一句话:“从c#语言角度来讲,饿汉式的单例类已经足够满足我们的需求了。”

附:单例模式程序例子

namespace 单例模式
{
    public sealed class singleton
    {
        private static readonly singleton sig = new singleton();
        private singleton() { }
        public static singleton GetInstance()
        {
            return sig;
        }
        public static void declare()
        {
            Console.WriteLine("我是单例模式!");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            singleton sig1 = singleton.GetInstance();
            singleton sig2 = singleton.GetInstance();
            if (sig1.Equals(sig2))
                Console.WriteLine("是同一个实例!");
            else
                Console.WriteLine("不是同一个实例!");
            Console.Read();
        }
    }
}

参考文献:

1.《大话设计模式》

2《设计模式之禅》

3.《HEAD FIRST 设计模式》

3.http://blog.sina.com.cn/s/blog_48a45b950100j68w.html

4.http://www.cnblogs.com/BoyXiao/archive/2010/05/07/1729376.html?login=1(建议也看看评论,评论更精彩)

5.http://blog.csdn.net/hackbuteer1/article/details/7460019

6.http://blog.csdn.net/zhengzhb/article/details/7331369

7.http://msdn.microsoft.com/zh-cn/library/c5kehkcz(VS.80).aspx

8.http://www.cnblogs.com/jeffreyzhao/archive/2009/09/02/double-check-failure.html
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值