在.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. a标签点击后,保证后来的样式

    在个人中心中,左侧的菜单(a标签),点击之后,不能够应用新的样式 而在静态界面html 中表现正常,在修改成动态的就不行了 可能是a标签链接经过了后台,当前页面的this对象发生了变化,所以就不能够像 ...

  2. LA 3026 && POJ 1961 Period (KMP算法)

    题意:给定一个长度为n字符串s,求它每个前缀的最短循环节.也就是对于每个i(2<=i<=n),求一个最大整数k>1(如果存在),使得s的前i个字符组成的前缀是某个字符串重复得k次得到 ...

  3. 9) 依赖查询 & 镜像站

    依赖查询 http://mvnrepository.com/ Maven仓库查询 http://search.maven.org 仓库 加上这两个,如果使用中央仓库 Eclipse 极有可能会卡死 & ...

  4. FuelPHP 简体中文手册

    FuelPHP中文手册 FuelPHP是一个简单的.灵活的.社区驱动的PHP 5.3 web框架,它基于其他框架的最佳思想,是一个全新的开始. 他的诞生源自于很多开发社区对于现有开发框架的不满,Fue ...

  5. 字符串转换成NSDate类型的 为nil解决方法

    方法一    通过下列函数来解决   但是得到的日期会改变 修改方法fix   - (NSDate *)timeForString:(NSString *)string {         NSMut ...

  6. c++ 内联函数 (讲解的TM真好)

    1.  内联函数 在C++中我们通常定义以下函数来求两个整数的最大值: 复制代码 代码如下: int max(int a, int b) {  return a > b ? a : b; } 为 ...

  7. C实现一个NTP客户端,可以从指定IP的NTP服务器获取时间戳

    参考::https://blog.csdn.net/dosthing/article/details/81588219 下面的代码测试通过 头文件 #include <sys/types.h&g ...

  8. 关于java的volatile关键字与线程栈的内容以及单例的DCL

    用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最新的值.volatile很容易被误用,用来进行原子性操作. package com.guangshan.test; pub ...

  9. Windows下VM安装MacOS

    我们在使用Windows操作系统的情况下,不去买苹果电脑,想去玩玩Mac苹果操作系统,有两种选择,一种就是安装黑苹果(就是在非苹果电脑上安装MacOS)这种方式不推荐,因为你会遇到很多很多不兼容问题, ...

  10. ListView的另一种可读性更强的ViewHolder模式写法

    常见的写法是这样的: @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHol ...