单例模式(Singleton)
意图
单例模式是一种创建型模式,它能确保一个类只有一个实例,并提供一个访问该实例的全局节点。
问题
- 单例模式同时解决了两个问题, 所以违反了单一职责原则: 保证一个类只有一个实例。 为什么会有人想要控制一个类所拥有的实例数量? 最常见的原因是控制某些共享资源(例如数据库或文件)的访问权限。 它的运作方式是这样的: 如果你创建了一个对象, 同时过一会儿后你决定再创建一个新对象, 此时你会获得之前已创建的对象, 而不是一个新对象。 注意, 普通构造函数无法实现上述行为, 因为构造函数的设计决定了它必须总是返回一个新对象。
- 为该实例提供一个全局访问节点。想想你曾用过的那些存储重要对象的全局变量,它们在使用上十分方便, 但同时也非常不安全,因为任何代码都有可能覆盖掉那些变量的内容,从而引发程序崩溃。 和全局变量一样, 单例模式也允许在程序的任何地方访问特定对象。 但是它可以保护该实例不被其他代码覆盖。 还有一点: 你不会希望解决同一个问题的代码分散在程序各处的。 因此更好的方式是将其放在同一个类中, 特别是当其他代码已经依赖这个类时更应该如此。
解决方案
一个类的对象永远只会在当前进程中被创建一次,也就是说构造函数只可能被调用一次,不论有多少线程调用。为什么需要单例,假如这个类是用来操作某个资源的,如果存在多个这个类的实例,这可能在操作这个资源的时候造成破坏,所以只能创建一个实例是很有必要的。
所有单例的实现都包含以下两个相同的步骤:
- 将默认构造函数设为私有, 防止其他对象使用单例类的
new
运算符。 - 新建一个静态构建方法作为构造函数。 该函数会 “偷偷” 调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中。 此后所有对于该函数的调用都将返回这一缓存对象。
如果你的代码能够访问单例类, 那它就能调用单例类的静态方法。 无论何时调用该方法, 它总是会返回相同的对象。
结构
- 单例(Singleton)类声明了一个名为
getInstance
获取实例的静态方法来返回其所属类的一个相同实例。 单例的构造函数必须对客户端(Client)代码隐藏。 调用获取实例方法必须是获取单例对象的唯一方式。
实现方式
- 在类中添加一个私有静态成员变量用于保存单例实例。
- 声明一个公有静态构建方法用于获取单例实例。
- 在静态方法中实现”延迟初始化”。该方法会在首次被调用时创建一个新对象,并将其存储在静态成员变量中。此后该方法每次被调用时都返回该实例。
- 将类的构造函数设为私有。 类的静态方法仍能调用构造函数, 但是其他对象不能调用。
- 检查客户端代码, 将对单例的构造函数的调用替换为对其静态构建方法的调用。
代码演示
一般来说,直接把对象声明为静态即可,程序集在加载过程中进行构造,这个也是线程安全的。但问题是如果此对象一直没有被调用,同时构造函数的开销较大,这个会造成资源浪费。
1234567891011121314151617 | class Singleton{ private static Singleton instance = new Singleton(); /// <summary> /// 构造函数声明为私有 /// </summary> private Singleton() { Console.WriteLine("执行构造函数"); } public static Singleton Get() { return instance; }} |
---|
著名的双检锁法,只在需要时执行构造函数,同时也是线程安全的
123456789101112131415161718192021222324252627282930313233 | class Singleton{ private static Singleton instance = null; private static readonly object lockobj = new object(); /// <summary> /// 构造函数声明为私有 /// </summary> private Singleton() { Console.WriteLine("执行构造函数"); } /// <summary> /// 双检锁 /// </summary> /// <param name="owner"></param> /// <returns></returns> public static Singleton Get() { if (instance == null) { lock (lockobj) { if (instance == null) { instance = new Singleton(); } } } return instance; }} |
---|
线程安全单例的完整代码示例:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 | using System;using System.Threading;namespace Singleton{ // This Singleton implementation is called "double check lock". It is safe // in multithreaded environment and provides lazy initialization for the // Singleton object. class Singleton { private Singleton() { } private static Singleton _instance; // We now have a lock object that will be used to synchronize threads // during first access to the Singleton. private static readonly object _lock = new object(); public static Singleton GetInstance(string value) { // This conditional is needed to prevent threads stumbling over the // lock once the instance is ready. if (_instance == null) { // Now, imagine that the program has just been launched. Since // there's no Singleton instance yet, multiple threads can // simultaneously pass the previous conditional and reach this // point almost at the same time. The first of them will acquire // lock and will proceed further, while the rest will wait here. lock (_lock) { // The first thread to acquire the lock, reaches this // conditional, goes inside and creates the Singleton // instance. Once it leaves the lock block, a thread that // might have been waiting for the lock release may then // enter this section. But since the Singleton field is // already initialized, the thread won't create a new // object. if (_instance == null) { _instance = new Singleton(); _instance.Value = value; } } } return _instance; } // We'll use this property to prove that our Singleton really works. public string Value { get; set; } } class Program { static void Main(string[] args) { // The client code. Console.WriteLine( "{0}\n{1}\n\n{2}\n", "If you see the same value, then singleton was reused (yay!)", "If you see different values, then 2 singletons were created (booo!!)", "RESULT:" ); Thread process1 = new Thread(() => { TestSingleton("FOO"); }); Thread process2 = new Thread(() => { TestSingleton("BAR"); }); process1.Start(); process2.Start(); process1.Join(); process2.Join(); } public static void TestSingleton(string value) { Singleton singleton = Singleton.GetInstance(value); Console.WriteLine(singleton.Value); } }} |
---|
执行结果:
12 | FOOFOO |
---|
参考原文:单例设计模式
相关文章
- 高并发下线程安全的单例模式(最全最经典)
- java单例模式_Java单例模式
- java单例模式——详解JAVA单例模式及8种实现方式
- Java单例模式以及其实现
- Activity启动模式之FLAG_ACTIVITY_CLEAR_TOP
- 零基础学习设计模式之装饰器模式(文档)
- 一、单例模式
- C++ 单例模式_c 单例模式
- 【说站】php单例模式如何理解
- 【说站】java单例中饿汉模式的使用
- 线程安全且按需构建的单例模式
- springbean生命周期通俗一点_spring为啥是单例模式
- 13. 多线程案例(1)——单例模式(饿汉模式/懒汉模式)
- 【Java】单例模式及指令重排问题
- PIL质押NFT分红模式系统开发讲解方案模式
- 单例模式-双重检查锁(DCL)和volatile 的应用
- 算法策略的主动选择,拒绝if...else...(策略模式+简单工厂模式)
- php设计模式(六):单例模式(Singleton)
- Java学习笔记之三十详解Java单例(Singleton)模式编程语言
- Java 单例模式的线程安全实现详解编程语言
- 单例模式(单例设计模式)详解
- 比Chrome更好用 Microsoft Edge性能模式下速度更快
- 如何设置 Linux 的 VGA 模式?(linuxvga)
- Oracle公司探索创新的商业经营模式(oracle公司经营方式)
- Redis集群模式哪一种最适合您(redis集群模式选择)
- php设计模式之单例模式
- 用jQuery做更好的组件通用组件定义模式
- php单例模式实现(对象只被创建一次)
- Python设计模式之单例模式实例
- php实现singleton()单例模式实例