1、单件模式简介
1.1>、定义
单件模式(Singleton)定义:要求一个类有且仅有一个实例,并且提供了一个全局的访问点,在同一时刻只能被一个线程所访问。
单件模式的特点:
1>、单件类只能有一个实例。
2>、单件类必须自身创建唯一实例。
3>、单件类必须给所有其它对象提供唯一实例。
1.2>、使用频率
中高
1.3>、单件模式应用
1>、每台计算机可以有若干个打印机,但只能有一个Printer Spooler,避免两个打印作业同时输出到打印机;
2>、Word文档在同一时间内,只能有一个用户对其进行操作,程序设计中需要保证一个文档不能被两个用户同时写入。
2、单件模式结构
2.1>、结构图
2.2>、参与者
单件模式参与者:
◊ Singleton
° 被调用的单件对象;
° 在单件模式中,通常由Instance()或GetInstance()方法负责对象的创建,该方法应保证每个需要(单件)对象的客户端均能访问。
3、单件模式结构实现
C#单件模式实现要点:
◊ 单件类有一个私有的无参构造函数,这可以防止被其他类实例化,而且单例类也不应该被继承,如果单例类允许继承那么每个子类都可以创建实例,这就违背了单件模式的“唯一实例”原则。
◊ 单件类使用sealed修饰,可以阻止被继承。
◊ 使用一个静态的变量用来保存单实例的引用。
◊ 使用一个公有的静态方法用来获取单一实例的引用,如果实例为null即创建一个。
1>、非线程安全
Singleton.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DesignPatterns.SingletonPattern.Structural { /// <summary> /// 单件模式实现方式:由于该实现方式非线程安全,在实际应用中不推荐使用。 /// </summary> public sealed class Singleton { private static Singleton _instance; // 将构造函数设为private,防止通过new实例化对象 private Singleton() { } // 获取实例,并加入判断逻辑,保证实例只被创建一次 public static Singleton Instance() { // 使用延迟初始化 // 注: 非线程安全 if (_instance == null) { _instance = new Singleton(); } return _instance; } } }
Program.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DesignPatterns.SingletonPattern.Structural; namespace DesignPatterns.SingletonPattern { class Program { static void Main(string[] args) { // 创建一个实例s1 Singleton s1 = Singleton.Instance(); // 创建一个实例s2 Singleton s2 = Singleton.Instance(); if (s1 == s2) { Console.WriteLine("对象为相同实例"); } } } }
运行输出:
对象为相同实例
请按任意键继续. . .
实践中Word文档在同一时间内,只能一个用户对其进行写操作实例。
Singleton.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DesignPatterns.SingletonPattern.Practical { /// <summary> /// Word文档在同一时间内,只能有一个用户对其进行操作,需要保证一个文档不能被两个用户同时写入。 /// </summary> public sealed class Singleton { private static Singleton _instance; // 将构造函数设为private,防止通过new实例化对象 private Singleton() { } // 获取实例,并加入判断逻辑,保证实例只被创建一次 public static Singleton Instance() { // 使用延迟初始化 // 注: 非线程安全 if (_instance == null) { _instance = new Singleton(); Console.WriteLine("Word文档打开成功,具有读写权限"); } else { Console.WriteLine("Word文档已经被锁定,不可写入"); } return _instance; } } }
Program.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DesignPatterns.SingletonPattern.Practical; namespace DesignPatterns.SingletonPattern { class Program { static void Main(string[] args) { // 创建一个实例s1 Singleton s1 = Singleton.Instance(); // 创建一个实例s2 Singleton s2 = Singleton.Instance(); Console.ReadKey(); } } }
运行输出:
Word文档打开成功,具有读写权限
Word文档已经被锁定,不可写入
以上的实现方式适用于单线程环境,在多线程的环境下有可能得到Singleton类的多个实例。假如同时有两个线程去判断(null == _singleton),并且得到的结果为真,那么两个线程都会创建类Singleton的实例,这样就违背了Singleton模式“唯一实例”的原则。
2>、简单线程安全
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DesignPatterns.SingletonPattern.Structural { public sealed class Singleton { private static Singleton _instance; // Lock synchronization object private static readonly object _syncLock = new object(); // Constructor is 'private' private Singleton() { } public static Singleton Instance() { // Support multithreaded applications through // 'Double checked locking' pattern which (once // the instance exists) avoids locking each // time the method is invoked if (_instance == null) { lock (_syncLock) { if (_instance == null) { _instance = new Singleton(); } } } return _instance; } } }
以上方式的实现方式是线程安全的,首先创建了一个静态只读的进程辅助对象,由于lock是确保当一个线程位于代码的临界区时,另一个线程不能进入临界区(同步操作)。如果其他线程试图进入锁定的代码,则它将一直等待,直到该对象被释放。从而确保在多线程下不会创建多个对象实例了。但这种实现方式要进行同步操作,将影响系统性能的瓶颈和增加了额外的开销。
4、单件模式应用分析
单件模式使用注意点:
1>、不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。
2>、不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放。
单件模式适用情形:
1>、当类只能有一个实例而且客户可以从一个众所周知的访问点访问时;
2>、当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能适用一个扩展的实例时。
单件模式具有以下特点:
1>、实例控制:Singleton会阻止其他对象实例化其自己的Singleton对象的副本,从而确保所有对象都访问唯一实例;
2>、额外的开销:虽然很小,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销;
3>、可能的开发混淆:使用Singleton对象时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类;
4>、对象的生存期问题:Singleton不能解决删除单个对象的问题。在提供内存管理的语言中(如.Net Framework的语言),只有Singleton类能够导致实例被取消分配,因为它包含该对象实例的私有引用。在某些语言中(如C++),其他类可以删除对象实例,但这样会导致Singleton类中出现悬浮引用。
5、参考资料