点击这里查看全部设计模式系列文章导航

单例(Singleton)模式介绍

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

单例模式的特点:

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

单例模式应用

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

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

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

单例模式代码示例

饿汉单例

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

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

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

懒汉式单例

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

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

单线程Singleton实现

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

  多线程实现:

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

    /// <summary>
/// 懒汉式单例
/// </summary>
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”判断,第二个线程并不知道实例已经创建,将继续创建新的实例,还是会产生多个单例对象,违背单例模式的设计思想,因此需要进行双重检查。

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

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

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

C#设计模式(1)-单例模式的更多相关文章

  1. 设计模式之单例模式(Singleton)

    设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...

  2. GJM : C#设计模式(1)——单例模式

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  3. java设计模式之单例模式(几种写法及比较)

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

  4. 每天一个设计模式-4 单例模式(Singleton)

    每天一个设计模式-4 单例模式(Singleton) 1.实际生活的例子 有一天,你的自行车的某个螺丝钉松了,修车铺离你家比较远,而附近的五金店有卖扳手:因此,你决定去五金店买一个扳手,自己把螺丝钉固 ...

  5. 设计模式之单例模式的简单demo

    /* * 设计模式之单例模式的简单demo */ class Single { /* * 创建一个本类对象. * 和get/set方法思想一样,类不能直接调用对象 * 所以用private限制权限 * ...

  6. 设计模式之单例模式——Singleton

                        设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...

  7. 10月27日PHP加载类、设计模式(单例模式和工厂模式)、面向对象的六大原则

    加载类可以使用include.require.require_once三种中的任意一种,每个关键字都有两种方法,但是这种方法的缺点是需要加载多少个php文件,就要写多少个加载类的方法.一般也就需要加载 ...

  8. java 23 - 2 设计模式之单例模式

    单例模式:保证类在内存中只有一个对象. 如何保证类在内存中只有一个对象呢?  A:把构造方法私有  B:在成员位置自己创建一个对象  C:通过一个公共的方法提供访问 单例模式之饿汉式: (一进来就造对 ...

  9. [转]JAVA设计模式之单例模式

    原文地址:http://blog.csdn.net/jason0539/article/details/23297037 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主 ...

  10. python_way,day8 面向对象【多态、成员--字段 方法 属性、成员修饰符、特殊成员、异常处理、设计模式之单例模式、模块:isinstance、issubclass】

    python_way day8 一.面向对象三大特性: 多态 二.面向对象中的成员 字段.方法属性 三.成员修饰符 四.特殊成员 __init__.__doc__.__call__.__setitem ...

随机推荐

  1. fdisk 非交互式创建 分区

    一. key 非交互式创建分区, 与 交互式创建分区区别不大. 使用 fdisk 的默认选项, 使用空行即可, 不用回车. 创建 主分区 和 扩展分区时, 需要注意 分区号 二. 创建主分区 fdis ...

  2. IE11中navigator.userAgent的变化

    在原来判断浏览器是否是ie时,我们可以根据navigator.userAgent中时候有MSIE,但是IE11进行变革,userAgent中不在包含MSIE字段, 在实际项目中,入到类似的在控制台报错 ...

  3. [leetcode-581-Shortest Unsorted Continuous Subarray]

    Given an integer array, you need to find one continuous subarray that if you only sort this subarray ...

  4. JavaScript中的栈及通过栈操作的实例

    <script> /*栈操作*/ function Stack() { this.dataStore = []; this.top = 0; this.push = push; this. ...

  5. 数据结构之网络流入门(Network Flow)简单小节

    网络流的相关定义: 源点:有n个点,有m条有向边,有一个点很特殊,只出不进,叫做源点. 汇点:另一个点也很特殊,只进不出,叫做汇点. 容量和流量:每条有向边上有两个量,容量和流量,从i到j的容量通常用 ...

  6. Docker部署DVWA

    上次在Docker手动配置了一个Ubuntu的Lamp镜像,这次来试验一下使用这个镜像部署一个简单的web应用吧. 首先从Lamp镜像运行一个容器 root@VM-149-127-debian:~/a ...

  7. Google调试工具

    f11 逐语句,到过程里,f10逐过程,跳到下一个off,f8调到下一个断点执行.

  8. 不用EF框架,完成完美实体映射,且便于维护!(AutoMapper,petapoco)

    最近,需要搭建一个新项目,在需求分析时确定数据库中需要创建多个存储过程.所以如果还是用原来EF框架生成ADO.net实体模型的话,不利于修改. 主要是解决以下两个问题: 1.比如前端需要一个值,如果存 ...

  9. [图形学] Chp14 GLU曲面裁剪函数程序示例及样条表示遗留问题

    样条表示这章已经看完,最后的GLU曲面裁剪函数,打算按书中的示例实现一下,其中遇到了几个问题. 先介绍一下GLU曲面裁剪函数的使用方法. 1 裁剪函数是成对出现的: gluBeginTrim和gluE ...

  10. js 设置下拉框的默认值

    设置下拉框的默认值,直接在option中增加selected就可以了.但是现在要使用JS来设置它的默认值,代码如下: <select name="aaa" id=" ...