zoukankan      html  css  js  c++  java
  • 三种方式构建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     }
  • 相关阅读:
    【Head First Servlets and JSP】笔记
    【Java Web】新手教程(转)
    【Java Web】入门资源整理
    【离散数学】网络资源整理
    【c++习题】【17/5/8】重载运算符
    【c++习题】【17/4/13】stack
    【笔记】css3实现网页平滑过渡效果...
    【Python】常用内建模块(卒)
    【Java】仿真qq尝试:用户注册(三)
    【Thinking in java, 4e】复用类
  • 原文地址:https://www.cnblogs.com/xuejietong/p/9016890.html
Copyright © 2011-2022 走看看