在.NET 4.0之后,.NET Framework中提供了一种安全的延迟加载类型Lazy
Lazy能够在多线程环境下,保证GetValue函数只执行一次,从而实现单例模式

在过去,实现单例模式我们通常使用二次判断锁,或者利用类的静态初始化函数
利用Lazy类型,能够简化这一过程,并且性能上更好

Lazy创建的时候可以指定线程安装模式,目前有两种模式,PublicationOnly,ExcutionAndPublication

PublicationOnly模式

                boxed = CreateValue(); //
if (boxed == null ||
Interlocked.CompareExchange(ref m_boxed, boxed, null) != null) //
{
boxed = (Boxed)m_boxed; //
}
else
{
m_valueFactory = ALREADY_INVOKED_SENTINEL; //
}

1.运行初始化函数,装箱到一个内部Box类型中,解决null值判断的问题,如果已经创建的情况,会返回null,该过程是线程不安全的

2.判断m_boxed是否为空,m_boxed是value保存的字段,如果等于空则设置为boxed,该方法能保证原子性,该过程是线程安全的

3.如果CreateValue返回空,表示其他线程已经创建有实例,则设置为已经创建好的实例

4.将初始化方法标记为已经初始化,一般发生在并发运行情况下,多次运行CreateValue

PublicationOnly模式下使用基于Interlocked.CompareExchange实现的乐观锁,该类包含了原子性方法 CAS(Compare and swap)

CAS是利用CPU提供的原子性指令来实现,不同运行时版本可能有不一样实现
Interlocked具体的实现在Native方法中,有兴趣的朋友可以通过coreclr/jvm代码查看具体实现

这种模式下,单例函数可能多次运行,但是最终能保证获取到的实例只有一个

ExcutionAndPublication模式下使用的是Volatile+MonitorMonitor就是lock语句的实现,Monitor实现在Native代码中,是重量级的锁

Monitor支持队列和线程睡眠,能够保证一整个方法块处于单线程执行状态

                object threadSafeObj = Volatile.Read(ref m_threadSafeObj); //强制从主内存空间同步变量到线程内存空间副本
bool lockTaken = false;
try
{
if (threadSafeObj != (object)ALREADY_INVOKED_SENTINEL) //此时会有多个线程获取到正确值,抢夺开始
Monitor.Enter(threadSafeObj, ref lockTaken); //尝试等待锁,进入成功设置lockTaken为true
else
Contract.Assert(m_boxed != null);
            //单线程代码块 Start
if (m_boxed == null) //没有设置值的情况
{
boxed = CreateValue(); //获取值
m_boxed = boxed; //设置到字段中
Volatile.Write(ref m_threadSafeObj, ALREADY_INVOKED_SENTINEL); //强制将线程内存空间副本写入到主内存空间
}
else // got the lock but the value is not null anymore, check if it is created by another thread or faulted and throw if so
{
boxed = m_boxed as Boxed;
if (boxed == null) // it is not Boxed, so it is a LazyInternalExceptionHolder
{
LazyInternalExceptionHolder exHolder = m_boxed as LazyInternalExceptionHolder;
Contract.Assert(exHolder != null);
exHolder.m_edi.Throw();
}
}
            //单线程代码块End
}
finally
{
if (lockTaken) //进入成功需要释放,避免死锁
Monitor.Exit(threadSafeObj);
}

线程安全单例最佳实践,C#中的Lazy是如何保证线程安全的的更多相关文章

  1. java双重检测或枚举类实现线程安全单例(懒汉模式)

    双重检测实现 /** * 懒汉模式->双重同步锁单例模式 */ public class SingletonExample5 { private SingletonExample5() { } ...

  2. muduo网络库源码学习————线程本地单例类封装

    muduo库中线程本地单例类封装代码是ThreadLocalSingleton.h 如下所示: //线程本地单例类封装 // Use of this source code is governed b ...

  3. Swift百万线程攻破单例(Singleton)模式

    一.不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法.单例的实现有多种方法,如下面: class SwiftSingleton { class var shared ...

  4. Java-多线程与单例

    最近在公司写需求时遇到了多线程与单例一同出现的情况. 这个时候想到的就是线程安全以及单例的定义了,虽然单例指的是在内存中它只有一份,但是并不是说就是线程安全的. 所以,我当时就到网上找了关于多线程下单 ...

  5. 不使用synchronized和lock 锁实现线程安全单例

    单例实现方式一,锁机制 public class Singleton { private static Singleton singleton=null; public Singleton() { } ...

  6. 【设计模式】单例设计模式的N中Java实现方法

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17359719 特点 单例模式的特点: 1.只能有一个实例: 2.必须自己创建自己的一个实例 ...

  7. java线程安全单例

    public class MySingleton { // 使用volatile关键字保其可见性 volatile private static MySingleton instance = null ...

  8. Spring中构造器、init-method、@PostConstruct、afterPropertiesSet孰先孰后,自动注入发生时间以及单例多例的区别、SSH线程安全问题

    首先明白,spring的IOC功能需要是利用反射原理,反射获取类的无参构造方法创建对象,如果一个类没有无参的构造方法spring是不会创建对象的.在这里需要提醒一下,如果我们在class中没有显示的声 ...

  9. 无锁,线程安全,延迟加载的单例实现(C#)

    单例(singleton)是非常常见,也非常有用的设计模式,当然了, 面试中也是经常会被问到的:)在几乎所有的项目中都能看到它的身影.简而言之,单例保证了一个自定义类型在整个程序的生命周期只被创建一次 ...

随机推荐

  1. docker入门实战

    基本概念 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上. Docker是一个重新定义了程序开发测试.交付和 ...

  2. HDU 1864 最大报销额 (DP-01背包问题)

    题意:中文题,你懂得. 析:拿过题目一看,本来以为是贪心,仔细一看不是贪心,其实是一个简单的01背包问题(DP),不过这个题的坑是在处理发票上,刚开始WA了一次. 分析一下什么样的发票是不符合要求的: ...

  3. [ IE浏览器兼容问题 ] Web Uploader 在IE、FireFox下点击上传没反应

    一.项目源码: > html > js 初始化插件: 事件绑定:avalon > web > 报错: - IE: 脚本缺少对象 - FireFox: js业务逻辑代码部分事件正 ...

  4. struts2从浅至深(三)拦截器

    一:拦截器概述 Struts2中的很多功能都是由拦截器完成的. 是AOP编程思想的一种应用形式. 二:拦截器执行时机                             interceptor表示 ...

  5. (最小生成树) Arctic Network -- POJ --2349

    链接: http://poj.org/problem?id=2349 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 1371 ...

  6. hdu 2049 不容易系列之考新郎 && 对错排的详解

    题目 错排:  当n个编号元素放在n个编号位置,错排的方法数记着D(n) ⒈把第n个元素放在一个位置,比如位置k,一共有(n-1)种方法: ⒉放编号为k的元素,这时有两种情况: 1°把它放到位置n,那 ...

  7. Oracle EBS - Setup: 配置文件Profile

    http://blog.csdn.net/lfl6848433/article/details/8696939 Oracle EBS - Setup: 配置文件Profile 1.诊断Diagnost ...

  8. TFS Java SDK使用指南

    [2018.3.6 更新] 最新版本的TFS Java SDK(14.123.1)支持Java SDK 1.6版本,可以从Oracle的官方网站(http://www.oracle.com/techn ...

  9. 阿里云ECS服务器windows环境下配置redis

    一.下载解压redis github下载地址:https://github.com/MSOpenTech/redis/tags 下载的是Redis-x64-3.2.100版本,Redis-x64-3. ...

  10. NetCore入门篇:(十二)在IIS中部署Net Core程序

    一.简介 微软已经为net在iis中的部署提供了良好的支持,在IIS中部署NetCore是一件很容易的事. 二.在IIS中部署Net Core程序 1.微软官方文档有详细说明.进入 2.如果你已经熟悉 ...