设计模式-1 单例模式 SingletonPattern
23种设计模式
1,Chain of Responsibility(职责链,对象模式)
单例模式(Singleton Pattern)【使用频率:★★★★★】
1.概述:
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
单例指的是只能存在一个实例的类,在C#中,更准确的说法是在每个AppDomain之中只能存在一个实例的类,它是软件工程中使用最多的几种模式之一。在第一个使用者创建了这个类的实例之后,其后需要使用这个类的就只能使用之前创建的实例,无法再创建一个新的实例。
一、非线程安全
public sealed class Singleton
{
private static Singleton instance = null;
private Singleton()
{
}
public static Singleton instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
这种方法不是线程安全的,会存在两个线程同时执行if (instance == null)并且创建两个不同的instance,后创建的会替换掉新创建的,导致之前拿到的reference为空。
二、简单的线程安全实现
public sealed class Singleton
{
private static Singleton instance = null;
private static readonly object padlock = new object();
private Singleton()
{
}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}
相比较于实现一,这个版本加上了一个对instance的锁,在调用instance之前要先对padlock上锁,这样就避免了实现一中的线程冲突,该实现自始至终只会创建一个instance了。但是,由于每次调用Instance都会使用到锁,而调用锁的开销较大,这个实现会有一定的性能损失。
注意:这里我们使用的是新建一个private的object实例padlock来实现锁操作,而不是直接对Singleton进行上锁。直接对类型上锁会出现潜在的风险,因为这个类型是public的,所以理论上它会在任何code里调用,直接对它上锁会导致性能问题,甚至会出现死锁情况。
Note: C#中,同一个线程是可以对一个object进行多次上锁的,但是不同线程之间如果同时上锁,就可能会出现线程等待,或者严重的会出现死锁情况。因此,我们在使用lock时,尽量选择类中的私有变量上锁,这样可以避免上述情况发生。
存在问题:
问题貌似得以解决,事实并非如此。如果使用以上代码来实现单例,还是会存在单例对象不唯一。原因如下:
假如在某一瞬间线程A和线程B都在调用Instance属性,此时instance对象为null值,均能通过instance == null的判断。由于实现了lock加锁机制,线程A进入lock锁定的代码中执行实例创建代码,线程B处于排队等待状态,必须等待线程A执行完毕后才可以进入lock锁定代码。但当A执行完毕时,线程B并不知道实例已经创建,将继续创建新的实例,导致产生多个单例对象,违背单例模式的设计思想,因此需要进行进一步改进,在lock中再进行一次(instance == null)判断,这种方式称为双重检查锁定(Double-Check Locking)。
三、双重验证的线程安全实现
public sealed class Singleton
{
private volatile static Singleton instance = null;
private static readonly object padlock = new object();
private Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance == null)
{
// 当第一个线程运行到这里时,此时会对locker对象 "加锁",
// 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,
// 该线程就会挂起等待第一个线程解锁
// 第一个线程运行完之后, 会对该对象"解锁"
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}
如果使用双重检查锁定来实现单例类,需要在静态成员变量instance之前增加修饰符volatile,被volatile修饰的成员变量可以确保多个线程都能够正确处理。
volatile的使用场景
多个线程同时访问一个变量,CLR为了效率,允许每个线程进行本地缓存,这就导致了变量的不一致性。volatile就是为了解决这个问题,volatile修饰的变量,不允许线程进行本地缓存,每个线程的读写都是直接操作在共享内存上,这就保证了变量始终具有一致性。
性能测试
为了比较这几种实现的性能,我做了一个小测试,循环拿这些实现中的单例9亿次,每次调用instance的方法执行一个count++操作,每隔一百万输出一次。

3. 模式优缺点
3.1 优点
- 提供了对唯一实例的受控访问;
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能;
- 可以根据实际情况需要,在单例模式的基础上扩展做出双例模式,多例模式;
3.2 缺点
- 单例类的职责过重,里面的代码可能会过于复杂,在一定程度上违背了“单一职责原则”。
- 如果实例化的对象长时间不被利用,会被系统认为是垃圾而被回收,这将导致对象状态的丢失。
4. 适用场景
- 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
- 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
设计模式-1 单例模式 SingletonPattern的更多相关文章
- iOS设计模式(02):单例模式
iOS设计模式(02):单例模式 singleton-design-pattern 什么是单例模式? 单例模式是一个类在系统中只有一个实例对象.通过全局的一个入口点对这个实例对象进行访问.在iOS开发 ...
- 设计模式之单例模式(Singleton)
设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...
- GJM : C#设计模式(1)——单例模式
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- java设计模式之单例模式(几种写法及比较)
概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...
- 每天一个设计模式-4 单例模式(Singleton)
每天一个设计模式-4 单例模式(Singleton) 1.实际生活的例子 有一天,你的自行车的某个螺丝钉松了,修车铺离你家比较远,而附近的五金店有卖扳手:因此,你决定去五金店买一个扳手,自己把螺丝钉固 ...
- 设计模式之单例模式的简单demo
/* * 设计模式之单例模式的简单demo */ class Single { /* * 创建一个本类对象. * 和get/set方法思想一样,类不能直接调用对象 * 所以用private限制权限 * ...
- 设计模式之单例模式——Singleton
设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...
- 10月27日PHP加载类、设计模式(单例模式和工厂模式)、面向对象的六大原则
加载类可以使用include.require.require_once三种中的任意一种,每个关键字都有两种方法,但是这种方法的缺点是需要加载多少个php文件,就要写多少个加载类的方法.一般也就需要加载 ...
- java 23 - 2 设计模式之单例模式
单例模式:保证类在内存中只有一个对象. 如何保证类在内存中只有一个对象呢? A:把构造方法私有 B:在成员位置自己创建一个对象 C:通过一个公共的方法提供访问 单例模式之饿汉式: (一进来就造对 ...
- [转]JAVA设计模式之单例模式
原文地址:http://blog.csdn.net/jason0539/article/details/23297037 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主 ...
随机推荐
- three.js 视频融合
MixVideo.js代码: //视频融合 import * as THREE from '../build/three.module.js'; import { API } from '../js. ...
- Python 在VSCode中使用
PyCharm到期了,所以打算换成VS Code(宇宙最好用编辑器?)试一试.但是下载之后不太会配置,网上不少的文章也比较旧了,所以打算自己看着官方教程配置一下.(VSCode版本:1.49.2,Py ...
- 涂色游戏Flood-it!(IDA star算法) - HDU 4127
做题之前,可以先到下面这个网站玩一会游戏: https://unixpapa.com/floodit/?sz=14&nc=6 游戏开发里面,比较常用的一个搜索算法是寻路算法,寻路算法里面用的最 ...
- 【教程】步兵 cocos2dx 加密和混淆
文章目录 摘要 引言 正文代码加密具体步骤代码加密具体步骤测试和配置阶段IPA 重签名操作步骤 总结 参考资料 摘要 本篇博客介绍了针对 iOS 应用中的 Lua 代码进行加密和混淆的相关技术.通过对 ...
- uni-app 预览pdf文件
安卓uni-app实现pdf文件预览功能: 1.https://mozilla.github.io/pdf.js/getting_started/#download下载 放在根目录下, 2.新建一个w ...
- webpack4中hash、chunkhash和contenthash三者的区别
https://blog.csdn.net/bubbling_coding/article/details/81561362
- hybird介绍
什么是hybird? hybrid即"混合",即前端和客户端的混合开发,需要前端开发人员和客户端开发人员配合完成. hybrid存在价值 可以快速迭代更新(无需app审核,思考为何 ...
- ASP.NET Core 5.0 MVC 页面标记帮助程序的使用
什么是标记帮助程序 标记帮助程序使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素.标记帮助程序使用 C# 创建,基于元素名称.属性名称或父标记以 HTML 元素为目标. 创建标记 ...
- python进度条实现的几种方法
一.普通进度条(time实现) import time def progress_bar(): for i in range(101): print(f'\rProgress: {"#&qu ...
- longjmp 使 C++ RAII 失效
C 语言的 longjmp 没有进行栈展开,而是直接跳转.从 longjmp 到 setjmp 之间的所有析构函数都没有调用,也就是 RAII 失效. #include <setjmp.h> ...