浅谈单例模式

devtools/2025/1/23 12:08:57/

前言

什么是单例模式?

单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)

上面是百度百科给出的解释。

大家都知道,面向对象的思想就是我们可以把一个类实例很多次,每次实例出来的都是一个对象,意味着你可以创建很多个基于这个类的对象。

单例模式,说白了,就是这些对象本质都是同一个,整个程序中,不管在哪里用,使用的都是同一个实例对象。

如果我们创建了一个China类,我们可以一直new吗?不可以,因为世界上只有一个China,所以我们使用的都是同一个China对象。

Version 1 - 非线程安全

    public class China{private China(){}private static China china = null;public static China Instance{get{if (china == null){Console.WriteLine("实例化对象");china = new China();}return china;}}}

最简单的实现方式如上图,创建一个私有的静态对象和私有构造方法,然后在CreateInstance方法里,加一个判断,如果为Null,就重新实例化一下,否则直接返回。

这种写法从逻辑上是没问题的,但是是否会出现这个if (china == null)判断,同时执行,这样就麻烦了。

所以这种写法在单线程的程序是没问题的,但是在多线程中,是可能会有问题的。

我们做个测试,测试代码如下:

    class Program{static void Main(string[] args){for (int i = 0; i < 10; i++){new TaskFactory().StartNew(() =>{China china =  China.Instance;});Thread.Sleep(10);}Console.ReadLine();}}

上面的代码,就是创建10个线程,都执行CreatInstance方法,那么最终是输出多少次Console.WriteLine("实例化对象")呢?

图片

图片

我们测试发现,这个输出结果是不唯一的,有时候会输出5次,有时候会输出2次,但是一般都是超过1次,这个就说明对象被多次实例化了,这就违背了单例模式的原则。

Version 2 - 简单的线程安全

既然出现问题,那么我们就需要做一下优化,优化之后的代码如下:

    public class China{private China(){}private static China china = null;private static object objlock = new object();public static China Instance{get{lock (objlock){Console.WriteLine("执行判断");if (china == null){Console.WriteLine("实例化对象");china = new China();}}return china;}}}

对比看下,就是加了一个同步锁,这样就可以避免同时执行的情况,并且,我们在lock里加了一个Console.WriteLine("执行判断"),观察这行代码执行多少次。

图片

从结果来看,实例化对象只执行了一次,说明对象只被创建过一次,满足了我们的需求,达到了预期的效果。

Version 3 - 双if+lock实现

上面那种方式已经可以达到预期效果,但是我们注意到一个问题,执行判断这行代码被执行了10次,这显示不符合我们的逻辑,既然已经实例化了,为什么每次还要执行判断呢?是不是多此一举?并且每次请求对象,都会进行lock操作,lock对性能是有一定影响的。

于是我们继续优化,优化之后的代码如下:

    public class China{private China(){}private static China china = null;private static object objlock = new object();public static China Instance{get{if (china == null){lock (objlock){Console.WriteLine("执行判断");if (china == null){Console.WriteLine("实例化对象");china = new China();}}}return china;}}}

我们对比代码可以看出,就是又加了一个if (china == null),这种双if+lock的方式,是不是可以解决我们的问题呢?

我们执行一次,看看结果:

图片

我们通过结果可以看到只执行了一次判断,也只执行一次实例化对象,但是我们还可以继续优化。

Version 4 - 静态变量实现

话不多说,直接上代码:

    public class China{private China(){}private static readonly China china = new China();public static China Instance{get{return china;}}}

利用静态变量去实现单例,非常简单,但同时也是线程安全的,由CLR保证,在程序第一次使用该类之前被调用,而且只调用一次。

但是这种方式也有缺点,就是实例化过程是在程序初始化时就执行的,而不是在使用时才执行,就是说,不管你用不用,都已经实例化了。

Version 5 - 完全懒汉式实现

    public class China{private China(){}public static China Instance{get{return Lazy.instance;}}private class Lazy{static Lazy(){}internal static readonly China instance = new China();}}

这种方法与上一种方法类似,只是多加了一个类,来解决上一个版本的缺点。

Version 6 - 使用Lazy特性

从.NET 4开始,可以使用Lazytype来实现完全懒汉式,代码也变得更简单,代码如下:

    public class China{private China(){}private static readonly Lazy<China> lazy = new Lazy<China>(() => new China());public static China Instance{get{return lazy.Value;}}}

整体总结

可能大家看完之后,选择困难症又会犯了吧?

这里给大家总结一下,除了Version 1,其他几种情况,均可以实现单例模式,一般情况下我们使用Vesion 2和Version 4比较多,虽然Version 2会浪费一定的资源,但是很容易理解,实际应用中,影响不会很大。


http://www.ppmy.cn/devtools/152876.html

相关文章

SpringBoot实现定时任务,使用自带的定时任务以及调度框架quartz的配置使用

SpringBoot实现定时任务&#xff0c;使用自带的定时任务以及调度框架quartz的配置使用 文章目录 SpringBoot实现定时任务&#xff0c;使用自带的定时任务以及调度框架quartz的配置使用一. 使用SpringBoot自带的定时任务&#xff08;适用于小型应用&#xff09;二. 使用调度框架…

linux系统安装vmware workstation

linux系统安装vmware workstation 1.下载vmware workstation2.安装vmware workstation&#xff08;使用root用户&#xff09;1.解压2.安装 3.启动vmware workstation 1.下载vmware workstation 访问 https://softwareupdate.vmware.com/cds/vmw-desktop/ws/17.6.2/24409262/li…

精通Python (13)

一&#xff0c;进程和线程 今天我们使用的计算机早已进入多CPU或多核时代&#xff0c;而我们使用的操作系统都是支持“多任务”的操作系统&#xff0c;这使得我们可以同时运行多个程序&#xff0c;也可以将一个程序分解为若干个相对独立的子任务&#xff0c;让多个子任务并发的…

手撕Diffusion系列 - 第四期 - Diffusion前向扩散

手撕Diffusion系列 - 第四期 - Diffusion前向扩散 目录 手撕Diffusion系列 - 第四期 - Diffusion前向扩散DDPM 原理图DDPM 前向扩散介绍Diffusion(前向扩散) 代码Part1 引入相关库函数Part2 去噪的一些参数初始化Part3 定义前向传播函数Part4 测试 参考 DDPM 原理图 ​ DDPM包…

【组件分享】商品列表组件-最佳实践

商品列表组件 商品列表组件用于展示商品信息列表&#xff0c;支持多种布局方式和自定义配置。 基础用法 <template><ProGoodsList :goods-list"goodsList" :layout"layout" item-click"handleItemClick" /> </template>&l…

Unreal Engine 5 C++ Advanced Action RPG 十一章笔记

第十一章 In Game Widgets 本章节就是做UI2-Template Button Widget 这章节创建不同的UI 结束UI胜利UI暂停菜单主菜单加载UI新建一个按钮小组件作为模版 3-Pause Menu Template Button 继续做更多模版UI 4-Lose Screen(游戏失败UI) 做失败的UI 之前按钮模版的调度程序就在这起…

Python中采用.add_subplot绘制子图的方法简要举例介绍

Python中采用.add_subplot绘制子图的方法简要举例介绍 目录 Python中采用.add_subplot绘制子图的方法简要举例介绍一、Python中绘制子图的方法1.1 add_subplot函数1.2 基本语法&#xff08;1&#xff09;add_subplot的核心语法&#xff08;2&#xff09;add_subplot在中编程中的…

【深度学习】常见模型-卷积神经网络(Convolutional Neural Networks, CNN)

卷积神经网络&#xff08;CNN&#xff09; 概念简介 卷积神经网络&#xff08;Convolutional Neural Networks, CNN&#xff09;是一种专门用于处理数据具有网格状拓扑结构&#xff08;如图像、语音&#xff09;的深度学习模型。它通过卷积操作从输入数据中提取局部特征&…