/// <summary>
/// 双检锁实现单例
/// </summary>
public sealed class SingletonDoubleCheck
{
//s_lock对象是实现线程安全所需要的,定义这个对象时,我们假设创建单例对象的代价高于创建一个System.Object对象
//并假设可能根本不需要创建单例对象,否则,更经济、更简单的做法是在一个类构造器中创建单例对象
private static Object s_lock = new Object(); //这个字段引用一个单例对象
private static SingletonDoubleCheck s_value = null; //私有构造器阻止这个类外部的任何代码创建实例
private SingletonDoubleCheck()
{ } //以下公共静态方法返回单例对象(如有必要就创建它)
public static SingletonDoubleCheck GetSingleton()
{
//如果单例对象已经创建,则直接返回它
if (s_value != null)
{
return s_value;
} //在指定对象上获取排他锁
Monitor.Enter(s_lock); //再次检查是否已经创建
//解决问题:若多个线程首次(单例对象还未创建)同时进入,此时,只有一个线程执行下面的代码(因为有Monitor),
//当该线程(第一个线程)创建完实例后,另一个线程(第二个线程)立即获得锁,也执行下面的代码,
//此时实例已经创建完成,后面的线程应该直接返回才对,因此再判断一次实例是否已经创建。
if (s_value == null)
{
//若仍未创建则创建它
SingletonDoubleCheck singleton = new SingletonDoubleCheck(); //将singleton给s_value
//下面的代码保证singleton中的引用只有在构造器结束执行之后才发布到s_value中
Volatile.Write(ref s_value, singleton); //注意:下面的写法是不牢靠的
//因为编译器可能会这样做:
//1.为SingletonDoubleCheck分配内存
//2.将引用发布到(赋给)s_value
//3.调用构造器
//假设在将引用发布给s_value之后,但在调用构造器之前,若有另一个线程调用了GetSingleton,
//此时s_value不为null,该线程会使用该对象,但该对象的构造器还没执行完成。
//s_value = new SingletonDoubleCheck();
} //释放指定对象上的排他锁
Monitor.Exit(s_lock); return s_value;
}
} /// <summary>
/// C#下简单的构造单例方法
/// CLR已保证了对类的构造是线程安全的,书写非常简便
/// 缺点也很明显,首次访问类的任何成员时都会调用类型构造器
/// 所以,如果该类定义了其它静态成员,就会在访问其它任何静态成员时创建该对象
/// </summary>
public sealed class SingletonSimple
{
private static SingletonSimple s_value = new SingletonSimple(); //私有构造器阻止这个类外部的任何代码创建实例
private SingletonSimple()
{ } //以下公共静态方法返回单例对象(如有必要就创建它)
public static SingletonSimple GetSingleton()
{
return s_value;
} //或
public static SingletonSimple SingletonInstance
{
get { return s_value; }
} //或
public static SingletonSimple Instance { get; } = new SingletonSimple();
} /// <summary>
/// 嵌套类实现单例
/// 如果多个线程同时调用GetSingleton,则可能创建多个SingletonNested对象
/// 但由于使用了Interlocked.CompareExchange,所以保证只会有一个引用被发布到s_value
/// 没有被固定下来的对象都会被垃圾回收
/// 该种方式不会阻塞线程
/// </summary>
public sealed class SingletonNested
{
private static SingletonNested s_value = null; //私有构造器阻止这个类外部的任何代码创建实例
private SingletonNested()
{ } //以下公共静态方法返回单例对象(如有必要就创建它)
public static SingletonNested GetSingleton()
{
//如果单例对象已经创建,则直接返回它
if (s_value != null)
{
return s_value;
} //创建一个新的单例对象,并把它固定下来(如果另一个线程还没有固定它的话)
SingletonNested singletonNested = new SingletonNested(); //比较两个指定的引用类型的实例 T 是否相等,如果相等,则替换第一个,并且返回s_value原始值
//s_value与null比较,如果相等则用singletonNested替换s_value,否则不替换
Interlocked.CompareExchange(ref s_value, singletonNested, null); //如果该线程竞争失败,则新建的第二个单实例对象会被垃圾回收 return s_value;
}
}

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

  1. 彻底了解构建 JSON 字符串的三种方式

    原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7701856.html 前言:JSON 是轻量级的数据交换格式,很常用,尤其是在使用 Ajax ...

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

    python实现单例模式的三种方式及相关知识解释 模块模式 装饰器模式 父类重写new继承 单例模式作为最常用的设计模式,在面试中很可能遇到要求手写.从最近的学习python的经验而言,singlet ...

  3. Solon 开发,三、构建一个Bean的三种方式

    Solon 开发 一.注入或手动获取配置 二.注入或手动获取Bean 三.构建一个Bean的三种方式 四.Bean 扫描的三种方式 五.切面与环绕拦截 六.提取Bean的函数进行定制开发 七.自定义注 ...

  4. C#批量插入数据到Sqlserver中的三种方式

    本篇,我将来讲解一下在Sqlserver中批量插入数据. 先创建一个用来测试的数据库和表,为了让插入数据更快,表中主键采用的是GUID,表中没有创建任何索引.GUID必然是比自增长要快的,因为你生 成 ...

  5. PHP的学习--连接MySQL的三种方式

    记录一下PHP连接MySQL的三种方式. 先mock一下数据,可以执行一下sql. /*创建数据库*/ CREATE DATABASE IF NOT EXISTS `test`; /*选择数据库*/ ...

  6. php 链接mysql的三种方式对比

    PHP连接Mysql的三种方式: 1.原生的连接方式  原生的连接方式是面向过程的写法 <?php $host = 'localhost'; $database = 'test'; $usern ...

  7. dubbo服务运行的三种方式

    dubbo服务运行,也就是让生产服务的进程一直启动.如果生产者进程挂掉,也就不存在生产者,消费者不能进行消费. Dubbo服务运行的三种方式如下:1.使用Servlet容器运行(Tomcat.Jett ...

  8. python 全栈开发,Day94(Promise,箭头函数,Django REST framework,生成json数据三种方式,serializers,Postman使用,外部python脚本调用django)

    昨日内容回顾 1. 内容回顾 1. VueX VueX分三部分 1. state 2. mutations 3. actions 存放数据 修改数据的唯一方式 异步操作 修改state中数据的步骤: ...

  9. Spring MVC异常统一处理的三种方式

    Spring 统一异常处理有 3 种方式,分别为: 使用 @ ExceptionHandler 注解 实现 HandlerExceptionResolver 接口 使用 @controlleradvi ...

随机推荐

  1. 跨服务器执行SQL

    --exec sp_helpserver 可以以存储过程形式执行以下: --1.1 创建登录信息(或叫创建链接服务器登录名映射)(只需选择一种方式) --1.1.1 以windows认证的方式登录 / ...

  2. 彻底搞定Javascript事件循环

    参考链接:https://juejin.im/post/5dca8a8be51d45227239abc4?utm_medium=hao.caibaojian.com&utm_source=ha ...

  3. ahk实现git图床自动预览以及转换markdown格式

    ahk实现git图床自动预览以及转换markdown格式 软件地址 https://gitee.com/layty/pic/tree/master/app 软件功能: 检测剪切板,如果剪切板有非文本信 ...

  4. Bootstrap手风琴悬浮下拉框,直接拷~~~

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Nginx03---重装

    1.先执行一下命令 1.1 删除nginx,–purge包括配置文件 sudo apt-get --purge remove nginx 1.2 自动移除全部不使用的软件包 sudo apt-get ...

  6. NIT校赛-- 雷顿女士与分队

    题意:https://ac.nowcoder.com/acm/contest/2995/D 思路: 和最大子串很像,dp[i]=max(dp[i-1]+a[i],a[i]),要不和前面连一起,要不就是 ...

  7. PostgreSQL练习3

    select dname,count(ename),avg(sal),sum(sal) from emp e,dept d where e.deptno=d.deptno group by dname ...

  8. 【Python】**kwargs和takes 1 positional argument but 2 were given

    Python的函数定义中可以在参数里添加**kwargs——简单来说目的是允许添加不定参数名称的参数,并作为字典传递参数.但前提是——你必须提供参数名. 例如下述情况: class C(): def ...

  9. 第9章:Python自动化管理

    1.使用SSH协议访问远程服务器 SSH协议 OpenSSH协议 使用密钥登陆远程服务器 使用ssh-agent管理私钥 2.使用Polysh批量管理服务器 Polysh requires pytho ...

  10. StoneTab标签页CAD插件 3.2.3

    //////////////////////////////////////////////////////////////////////////////////////////////////// ...