博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#设计模式(1)-单例模式
阅读量:5978 次
发布时间:2019-06-20

本文共 3372 字,大约阅读时间需要 11 分钟。

单例(Singleton)模式介绍

单例模式:也可以叫单件模式,官方定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式的特点:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其它对象提供这一实例。

单例模式应用

介绍完单例模式,大家肯定会有疑问为什么会有单例模式?

单例模式在实际场景中的应用:

  •  操作系统中只能有一个任务管理器,大家可以试试,操作系统同时只能打开一个任务管理器  。  
  • 每台计算机可以有若干个打印机,但只能有一个Printer Spooler,避免两个打印作业同时输出到打印机。

单例模式代码示例

饿汉单例

饿汉式单例类(Eager Singleton)是实现起来最容易的单例类;由于在定义静态变量的时候实例化单例类,因此在类加载时单例对象就已创建;

当类被加载时,静态变量instance 会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。

///     /// 饿汉式单例类    ///     public class EagerSingleton    {        // 定义一个静态变量来保存类的实例        private static EagerSingleton uniqueInstance = new EagerSingleton();        // 定义私有构造函数,使外界不能创建该类实例        private EagerSingleton()        {        }        ///         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点        ///         /// 
public static EagerSingleton GetInstance() { return uniqueInstance; } }

懒汉式单例

与饿汉式单例类相同之处是,懒汉式单例类(Lazy Singleton)的构造函数也是私有的。

与饿汉式单例类不同的是,懒汉式单例类在第一次被引用时将自己实例化,在懒汉式单例类被加载时不会将自己实例化。

单线程Singleton实现

///     /// 单例模式    ///     public class Singleton    {        // 定义一个静态变量来保存类的实例        private static Singleton uniqueInstance;// 定义私有构造函数,使外界不能创建该类实例        private Singleton()        {        }        ///         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点        ///         /// 
public static Singleton GetInstance() { // 如果类的实例不存在则创建,否则直接返回 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }

  多线程实现:

    如果在高并发、多线程环境下实现单例类,在某一时刻可能会有多个线程需要使用单例对象,即会有多个线程同时调用GetInstance()方法,可能会造成创建多个实例对象,这将违背单例模式的设计意图。为了防止生成多个单例对象,需要使用C#语言中的lock关键字,lock关键字锁定的代码片段称之为临界区,可以确保当一个线程位于代码的临界区时,另一个线程不能进入临界区。如果其他线程试图进入锁定的代码,则将一直等待,直到该对象被释放为止。修改之后的懒汉式单例类代码如下:

 

///     /// 懒汉式单例    ///     public class LazySingleton    {        private static LazySingleton instance = null;        //程序运行时创建一个静态只读的辅助对象          private static readonly object syncRoot = new object();        private LazySingleton() { }        public static LazySingleton GetInstance()        {            //第一重判断,先判断实例是否存在,不存在再加锁处理              if (instance == null)            {                //加锁的程序在某一时刻只允许一个线程访问                  lock (syncRoot)                {                    //第二重判断                      if (instance == null)                    {                        instance = new LazySingleton();  //创建单例实例                      }                }            }            return instance;        }    }

 

    在上面给出的多线程下的懒汉式单例类实现代码中,对静态工厂方法GetInstance()中创建单例对象的代码进行了加锁,由于在调用时无法确定该单例对象是否已创建,因此需要使用辅助对象syncRoot来进行代码锁定。为了不影响程序的性能,此处只锁定创建单例对象的代码,并未锁定整个方法。如果实例存在则直接返回,如果实例未创建则加锁后再创建。

      为了更好地对单例对象的创建进行控制,此处使用了一种被称之为双重检查锁定(Double-CheckLocking)的双重判断机制。在双重检查锁定中,当实例不存在且同时有两个线程调用GetInstance()方法时,它们都可以通过第一重“instance==null”判断,然后由于lock锁定机制,只有一个线程进入lock中执行创建代码,另一个线程处于排队等待状态,必须等待第一个线程执行完毕后才可以进入lock锁定的代码,如果此时不进行第二重“instance==null”判断,第二个线程并不知道实例已经创建,将继续创建新的实例,还是会产生多个单例对象,违背单例模式的设计思想,因此需要进行双重检查。

 

饿汉式单例类与懒汉式单例类比较

       饿汉式单例类在类被加载时就将自己实例化,它的优点在于无须考虑多个线程同时访问的问题,可以确保实例的唯一性;从调用速度和反应时间角度来讲,由于单例对象一开始就得以创建,因此要优于懒汉式单例。但是无论系统在运行时是否需要使用该单例对象,由于在类加载时该对象就需要创建,因此从资源利用效率角度来讲,饿汉式单例不及懒汉式单例,而且在系统加载时由于需要创建饿汉式单例对象,加载时间可能会比较长。

      懒汉式单例类在第一次使用时创建,无须一直占用系统资源,实现了延迟加载,但是必须处理好多个线程同时访问的问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费大量时间,这意味着出现多线程同时首次引用此类的机率变得较大,需要通过双重检查锁定等机制进行控制,这将导致系统性能受到一定影响。

 

转载于:https://www.cnblogs.com/yx007/p/SingletonPattern.html

你可能感兴趣的文章
为什么你的企业需要使用聊天机器人来在市场竞争中保持不败之地
查看>>
IdleHandler,页面启动优化神器
查看>>
ES6 学习笔记 ( let 和 const )
查看>>
Android 源码分析之旅4 1 Android HAL层概述
查看>>
面试总结3 介绍spring
查看>>
浅谈Nginx服务器的内部核心架构设计
查看>>
深入vue2.0底层思想--模板渲染
查看>>
webpack4.0入门指南(一)安装和转换es6语法
查看>>
理解消息转发机制
查看>>
深入浅出HTTP
查看>>
使用 Docker 搭建 Laravel 本地环境
查看>>
android常用设计模式之单例模式
查看>>
从零开始搭建一个 Webpack 开发环境配置(附 Demo)
查看>>
RAC 使用方法总结
查看>>
深度解析国内首个云原生数据库POLARDB的“王者荣耀”
查看>>
浏览器打印--类似快递拆包多个单号打印
查看>>
回顾·深度学习的可解释性与低频事件学习在金融领域的研究与应用
查看>>
逆向直播盒子MT·Box-iOS客户端
查看>>
JavaScript异步编程的6种方法
查看>>
10分钟理解TCP、UDP 和端口号
查看>>