单例(Singleton)模式

简介:

 单例模式(Singleton)可以说是最简单的模式,对.net来说,因为不需要考虑到垃圾回收机制,实现起来很简单,但是对于没有提供内存管理的平台来说,比如C++,因为单例模式只考虑创建对象,所以使用的时候要考虑全面些。

  其实说到些设计模式,我们有时候用到的真的很少,就像飞机零部件的模具不适用于汽车制造一样,某些设计模式也只在特定的环境下使用,单例模式的使用场景一般是资源管理器等,像说的最多的就是打印机场景:每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。说白点就是一个男人可以有很多女朋友,但是结婚生子的只能是其中一个。一夫多妻的情况就不是单例模式了,那应该是“多态”了。哈哈。

简单实现

  单例模式(Singleton)在.net中的定义是:一个类有且仅有一个实例,并且自行实例化向整个系统提供。

  从定义中我们可以看出,单例模式所具有的三个要点:

  • 某个类只能有一个实例
  • 必须自行创建这个实例
  • 必须自行向整个系统提供这个实例

  根据所说的要点,我们可以在.net中这样简单的实现:

复制代码
 1     public class SingletonTest
 2     {
 3         public static SingletonTest model;
 4         private SingletonTest()
 5         { }
 6         public static SingletonTest getSingleton()
 7         {
 8             if (model==null)
 9             {
10                 model = new SingletonTest();
11             }
12             return model;
13         }
14     }
复制代码

   代码就这么简单,在getSingleton()方法返回实例的时候要先判断对象是否已经被实例化,如果是就不需要重新创建了。

线程安全

  上面的代码看起来没什么问题,但是在多线程的情况下就会出现问题,我们来开几个线程测试下:

复制代码
 1     public class SingletonTest
 2     {
 3         public static SingletonTest model;
 4         private SingletonTest()
 5         { }
 6         public static SingletonTest getSingleton()
 7         {
 8             if (model==null)
 9             {
10                 Console.WriteLine(String.Format("我是被线程:{0}创建的!", Thread.CurrentThread.Name));
11                 model = new SingletonTest();
12             }
13             return model;
14         }
15     }
复制代码
复制代码
 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Program p1 = new Program();
 6             p1.Test();
 7             Console.ReadLine();
 8         }
 9 
10         private void Test()
11         {
12             Thread newThread;
13             ThreadStart ts = new ThreadStart(DoWork);
14             for (int counter = 1; counter < 6; counter++)
15             {
16                 newThread = new Thread(ts);
17                 newThread.Name = "蟋蟀" + counter;
18                 newThread.Start();
19             }
20         }
21 
22         protected void DoWork()
23         {
24             //调用返回对象方法
25             SingletonTest.getSingleton();
26         }
27     }
复制代码

  执行结果:

  根据上图的执行结果,会发现SingletonTest对象被实例化了2次,按照单例模式(Singleton)的特性:一个类只能有一个实例,那就不是单例模式了,为什么会实例化两次呢?因为我们的计算机执行速度很快,在某一个时间点,线程1在执行完if (model==null)这段代码,还没执行model = new SingletonTest(),线程2刚好执行判断对象是否null,就是说线程1和线程2都会进入下面的if判断体中实例化对象。

  关于单例模式的线程安全问题,网上一找一大堆,在《漫谈设计模式》这本书中,作者也提到了线程安全问题,java中是使用的是“Double-Check Locking”方法,还有序列化的问题,这边先不考虑,其实在.net中解决线程安全的问题也很简单,就是用lock锁,我们根据上面的代码,再来修改下,然后做个测试:

复制代码
 1     public class SingletonTest
 2     {
 3         private static SingletonTest singleton;
 4         private static readonly object syncObject = new object();
 5         /// <summary>
 6         /// 构造函数必须是私有的
 7         /// 这样在外部便无法使用 new 来创建该类的实例
 8         /// </summary>
 9         private SingletonTest()
10         { }
11         /// <summary>
12         /// 定义一个全局访问点
13         /// 设置为静态方法
14         /// 则在类的外部便无需实例化就可以调用该方法
15         /// </summary>
16         /// <returns></returns>
17         public static SingletonTest getSingleton()
18         {
19             //这里可以保证只实例化一次
20             //即在第一次调用时实例化
21             //以后调用便不会再实例化
22             //第一重 singleton == null
23             if (singleton == null)
24             {
25                 lock (syncObject)
26                 {
27                     //第二重 singleton == null
28                     if (singleton == null)
29                     {
30                         Console.WriteLine(String.Format("我是被线程:{0}创建的!", Thread.CurrentThread.Name));
31                         singleton = new SingletonTest();
32                     }
33                 }
34             }
35             return singleton;
36         }
37     }
复制代码

   执行结果:

  从上面的执行结果我们就可以看到,对象仅被实例化了一次,在某段代码体中,只能有且只有一个线程访问,加锁的目的就好比:我们去火车站售票大厅买票,因为买票的人太多,为了缓解压力,就多开了几个售票窗口(线程),比如南京到徐州的G110次列车只有一张票,窗口A和窗口B的人同时都在买这一班次的票,这时候就要加锁,不然就有可能会出现只有一张票,但是卖出去两张。话题跑偏了,哈哈。

  示例代码下载:Singleton.rar

后记

  关于模式,再多说两句,在某些情况下,像上面所说的:模式可以看成现实生活中的模具,有些产品(项目)是由一种模具(模式)生成出来的,比如杯子、瓶子等,有些产品(项目)是由多种模具(模式)生成出来,然后组合而成的,比如汽车、飞机等,是由成千上万个零部件组合形成的。就是说学会模式后要会懂得组合,而且要“合适”的组合,这样才会做出一个完善的产品(项目)。

  还是那就话:骚年们,和小菜一起整理学习吧,未完待续。。。


本文转自田园里的蟋蟀博客园博客,原文链接:http://www.cnblogs.com/xishuai/p/3509346.html,如需转载请自行联系原作者

相关文章
|
安全 C#
单例模式(Singleton)
单例模式(Singleton)
70 0
@Singleton和@ApplicationScoped的区别
在JakartaEE的CDI标准中@Singleton和@ApplicationScoped的区别
441 0
|
设计模式 存储 安全
|
SQL 设计模式 安全
一个单例还能写出花来吗?
单例可以说是最简单的一个设计模式了,单例模式要求只能创建一个对象实例。通常的写法是声明私有的构造函数,提供静态方法获取单例的对象实例。 常见的单例写法就是饿汉式、懒汉式、双重加锁验证、静态内部类和枚举的方式,写法可能大家都知道,不过针对不同的写法还是有可以继续深挖一下的地方,让我们从最简单的几种写法开始回顾单例,不想看前面的话直接往后翻好了。
一个单例还能写出花来吗?
|
设计模式 Java
浅谈单例模式(Singleton)
单例模式的介绍 优点 缺点 Synchronized Synchronized示例 Synchronized与非Synchronized Singleton 第一个示例 第二个示例 第三个示例 第四个示例 第五个示例
143 0
|
测试技术 iOS开发 开发者
你真的能写好一个单例么?
单例可能是 iOS 开发者最熟悉设计模式之一了。 我们的项目里头也使用了很多单例。 最近为了解决项目中单例的 bug 而花费了两天多的时间,发现用 ObjC 写好一个单例真的不容易! V1.
806 0
|
安全 Java Spring
你写的单例一定安全吗?
前言: 在并发环境中,我们可以用各种锁来保持单例的线程安全,当然这是从业务角度来考虑的;但是,从一个攻击者的角度来看,你的单例也许只保证了线程安全,当攻击者通过反射new出单例的实例时候(反射的可以改变你的私有构造函数)...
1028 0
单例模式(singleton)
转载 确保对象的唯一性——单例模式 (一)确保对象的唯一性——单例模式 (二)确保对象的唯一性——单例模式 (三)确保对象的唯一性——单例模式 (四)确保对象的唯一性——单例模式 (五)
927 0