三种方式构建C#单例模式

  1     /// <summary>
  2     /// 双检锁实现单例
  3     /// </summary>
  4     public sealed class SingletonDoubleCheck
  5     {
  6         //s_lock对象是实现线程安全所需要的,定义这个对象时,我们假设创建单例对象的代价高于创建一个System.Object对象
  7         //并假设可能根本不需要创建单例对象,否则,更经济、更简单的做法是在一个类构造器中创建单例对象
  8         private static Object s_lock = new Object();
  9
 10         //这个字段引用一个单例对象
 11         private static SingletonDoubleCheck s_value = null;
 12
 13         //私有构造器阻止这个类外部的任何代码创建实例
 14         private SingletonDoubleCheck()
 15         {
 16
 17         }
 18
 19         //以下公共静态方法返回单例对象(如有必要就创建它)
 20         public static SingletonDoubleCheck GetSingleton()
 21         {
 22             //如果单例对象已经创建,则直接返回它
 23             if (s_value != null)
 24             {
 25                 return s_value;
 26             }
 27
 28             //在指定对象上获取排他锁
 29             Monitor.Enter(s_lock);
 30
 31             //再次检查是否已经创建
 32             //解决问题:若多个线程首次(单例对象还未创建)同时进入,此时,只有一个线程执行下面的代码(因为有Monitor),
 33             //当该线程(第一个线程)创建完实例后,另一个线程(第二个线程)立即获得锁,也执行下面的代码,
 34             //此时实例已经创建完成,后面的线程应该直接返回才对,因此再判断一次实例是否已经创建。
 35             if (s_value == null)
 36             {
 37                 //若仍未创建则创建它
 38                 SingletonDoubleCheck singleton = new SingletonDoubleCheck();
 39
 40                 //将singleton给s_value
 41                 //下面的代码保证singleton中的引用只有在构造器结束执行之后才发布到s_value中
 42                 Volatile.Write(ref s_value, singleton);
 43
 44                 //注意:下面的写法是不牢靠的
 45                 //因为编译器可能会这样做:
 46                 //1.为SingletonDoubleCheck分配内存
 47                 //2.将引用发布到(赋给)s_value
 48                 //3.调用构造器
 49                 //假设在将引用发布给s_value之后,但在调用构造器之前,若有另一个线程调用了GetSingleton,
 50                 //此时s_value不为null,该线程会使用该对象,但该对象的构造器还没执行完成。
 51                 //s_value = new SingletonDoubleCheck();
 52             }
 53
 54             //释放指定对象上的排他锁
 55             Monitor.Exit(s_lock);
 56
 57             return s_value;
 58         }
 59     }
 60
 61     /// <summary>
 62     /// C#下简单的构造单例方法
 63     /// CLR已保证了对类的构造是线程安全的,书写非常简便
 64     /// 缺点也很明显,首次访问类的任何成员时都会调用类型构造器
 65     /// 所以,如果该类定义了其它静态成员,就会在访问其它任何静态成员时创建该对象
 66     /// </summary>
 67     public sealed class SingletonSimple
 68     {
 69         private static SingletonSimple s_value = new SingletonSimple();
 70
 71         //私有构造器阻止这个类外部的任何代码创建实例
 72         private SingletonSimple()
 73         {
 74
 75         }
 76
 77         //以下公共静态方法返回单例对象(如有必要就创建它)
 78         public static SingletonSimple GetSingleton()
 79         {
 80             return s_value;
 81         }
 82
 83         //或
 84         public static SingletonSimple SingletonInstance
 85         {
 86             get { return s_value; }
 87         }
 88
 89         //或
 90         public static SingletonSimple Instance { get; } = new SingletonSimple();
 91     }
 92
 93     /// <summary>
 94     /// 嵌套类实现单例
 95     /// 如果多个线程同时调用GetSingleton,则可能创建多个SingletonNested对象
 96     /// 但由于使用了Interlocked.CompareExchange,所以保证只会有一个引用被发布到s_value
 97     /// 没有被固定下来的对象都会被垃圾回收
 98     /// 该种方式不会阻塞线程
 99     /// </summary>
100     public sealed class SingletonNested
101     {
102         private static SingletonNested s_value = null;
103
104         //私有构造器阻止这个类外部的任何代码创建实例
105         private SingletonNested()
106         {
107
108         }
109
110         //以下公共静态方法返回单例对象(如有必要就创建它)
111         public static SingletonNested GetSingleton()
112         {
113             //如果单例对象已经创建,则直接返回它
114             if (s_value != null)
115             {
116                 return s_value;
117             }
118
119             //创建一个新的单例对象,并把它固定下来(如果另一个线程还没有固定它的话)
120             SingletonNested singletonNested = new SingletonNested();
121
122             //比较两个指定的引用类型的实例 T 是否相等,如果相等,则替换第一个,并且返回s_value原始值
123             //s_value与null比较,如果相等则用singletonNested替换s_value,否则不替换
124             Interlocked.CompareExchange(ref s_value, singletonNested, null);
125
126             //如果该线程竞争失败,则新建的第二个单实例对象会被垃圾回收
127
128             return s_value;
129         }
130     }

原文地址:https://www.cnblogs.com/xuejietong/p/9016890.html

时间: 2024-10-07 09:17:21

三种方式构建C#单例模式的相关文章

python实现单例模式的三种方式及相关知识解释

python实现单例模式的三种方式及相关知识解释 模块模式 装饰器模式 父类重写new继承 单例模式作为最常用的设计模式,在面试中很可能遇到要求手写.从最近的学习python的经验而言,singleton实现的四种方法都是python的重要特征,反过来也刚好是几种特征的最佳实现.(比如你平常开发中很难遇到几个需要写元类的地方)如果不能随手写出某种实现,说明你对于那种实现的概念还没有完全掌握.最近场通过写装饰器模式的singleton来复习装饰器概念. 1. module实现 #模块实现 from

数据导入HBase最常用的三种方式及实践分析

数据导入HBase最常用的三种方式及实践分析         摘要:要使用Hadoop,需要将现有的各种类型的数据库或数据文件中的数据导入HBase.一般而言,有三种常见方式:使用HBase的API中的Put方法,使用HBase 的bulk load工具和使用定制的MapReduce Job方式.本文均有详细描述. [编者按]要使用Hadoop,数据合并至关重要,HBase应用甚广.一般而言,需要 针对不同情景模式将现有的各种类型的数据库或数据文件中的数据转入至HBase 中.常见方式为:使用H

android客户端与服务端交互的三种方式

android客户端向服务器通信一般有以下选择: 1.传统的java.net.HttpURLConnection类 2.apache的httpClient框架(已纳入android.jar中,可直接使用) 3.github上的开源框架async-http(基于httpClient) ---------------------------------------------------------------------------------- 下面分别记录这三种方式的使用, 传统方式: /**

解析Xml文件的三种方式及其特点

解析Xml文件的三种方式 1.Sax解析(simple api  for xml) 使用流式处理的方式,它并不记录所读内容的相关信息.它是一种以事件为驱动的XML API,解析速度快,占用内存少.使用回调函数来实现. 1 class MyDefaultHander extends DefaultHandler{ 2 private List<Student> list; 3 private Student student; 4 5 @Override 6 public void startDo

【Spring】创建对象的三种方式

关于Spring的搭建可参见:浅析Spring框架的搭建.在测试之前还是应该先将环境配置好,将相关Jar包导进来.Spring创建的对象,默认情况下都是单例模式,除非通过scope指定. 一.通过构造函数创建对象. 2.1 利用无参构造函数+setter方法注入值 最基本的对象创建方式,只需要有一个无参构造函数(类中没有写任何的构造函数,默认就是有一个构造函数,如果写了任何一个构造函数,默认的无参构造函数就不会自动创建哦!!)和字段的setter方法. Person类: package com.

(转)maven怎么 引入(或引用/使用) 自定义(或本地/第三方) jar的三种方式 图文教程 方法二最简单

转:https://blog.csdn.net/wabiaozia/article/details/52798194 准备工作: 假如我有一个自定义jar是:123456.jar,下载地址http://download.csdn.net/detail/wabiaozia/9870838 如果不想下载,可以按照https://jingyan.baidu.com/article/046a7b3ed8b23ef9c27fa9b9.html 操作即可得到jar. jar包里的源码是: public cl

多线程的实现三种方式

多线程的实现三种方式:1 继承thread类,重写run方法 继承thread方法就可以i调用thread类的start方法,,start方法调用java natvie start0();这个是调用操作系统的方法,start方法 package com.cxy; class Mythread01 extends Thread{ @Override public void run(){ System.out.println("iii"); }} public class Mythread

CentOS安装docker ce的三种方式

参考文章: CentOS安装docker ce的三种方式: 1.环境 CentOS Linux release 7.6.1810 (Core) 2.卸载旧版本 sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker

AngularJs学习——实现数据绑定的三种方式

三种方式: 方式一:<h5>{{msg}}</h5>  此方式在页面刷新的时候会闪现{{}} 方式二:<h5 ng-bind="msg"></h5> 方式三:<h5 ng-clock class="ng-clock">{{msg}}</h5> 示例代码: <!DOCTYPE html> <html lang="en" ng-app="myapp&q