• 设计模式之单例模式(2)


    设计模式之单例模式(1)中给出了Java中的双检锁技术方案

    为什么需要volatile

    设计模式之单例模式(1)中给出了Java中的双检锁技术方案。Javaz中为什么一定要用volatile,如果不用的话,会存在什么问题。

    private static volatile Singleton instance
    
    • 1

    前面的双重检查示例代码创建一个对象。这一行代码可以分解为如下的3行伪代码。

    memory=allocate();        //1:分配对象的内存空间
    ctorInstance(memory);     //2:初始化对象
    instance = memory;          //3:设置instance指向刚分配的内存地址
    
    • 1
    • 2
    • 3

    上面3行伪代码中的2和3之间,可能会被重排序。2和3重排序之后的执行时序如下。

    memory=allocate();        //1:分配对象的内存空间
    instance = memory;          //3:设置instance指向刚分配的内存地址
                                //注意,此时对象还没有被初始化!
    ctorInstance(memory);     //2:初始化对象
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    由于单线程内要遵守intra-thread semantics,从而能保证A线程的执行结果不会被改变。但是,当线程A和B按上图时序执行时,B线程将看到一个还没有被初始化的对象。而使用volatile可以禁止这种重新排序

    双检锁技术

    这篇文章给出C#版本的双检锁技术代码:

    namespace Singleton6
    {
        internal sealed class LazySingleton
        {
            private static volatile LazySingleton singleton = null;
            private static Object m_obj = new();
            
           //私有构造器阻止这个类的任何外部代码创建实例
            private LazySingleton()
            {
            }
            public static LazySingleton GetLazySingleton()
            {
                if (singleton == null)
                {
                    Monitor.Enter(m_obj);
                    if (singleton == null)
                    {
                        singleton = new LazySingleton();
                    }
                    Monitor.Exit(m_obj);
                }
                return singleton;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    反编译的il代码如下:

    // Type: Singleton6.LazySingleton 
    // Assembly: Singleton6, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
    // MVID: 1CB2B007-5286-449F-BF1D-C02A34D9C212
    // Location: D:\vs2022\Project\Singleton6\bin\Debug\net6.0\Singleton6.dll
    // Sequence point data from D:\vs2022\Project\Singleton6\bin\Debug\net6.0\Singleton6.pdb
    
    .class private sealed auto ansi beforefieldinit
      Singleton6.LazySingleton
        extends [System.Runtime]System.Object
    {
      .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor([in] unsigned int8)
        = (01 00 01 00 00 ) // .....
        // unsigned int8(1) // 0x01
      .custom instance void System.Runtime.CompilerServices.NullableAttribute::.ctor([in] unsigned int8)
        = (01 00 00 00 00 ) // .....
        // unsigned int8(0) // 0x00
    
      .field private static class Singleton6.LazySingleton modreq ([System.Runtime]System.Runtime.CompilerServices.IsVolatile) singleton
    
      .field private static object m_obj
    
      .method private hidebysig specialname rtspecialname instance void
        .ctor() cil managed
      {
        .maxstack 8
    
        // [15 9 - 15 32]
        IL_0000: ldarg.0      // this
        IL_0001: call         instance void [System.Runtime]System.Object::.ctor()
        IL_0006: nop
    
        // [16 9 - 16 10]
        IL_0007: nop
    
        // [19 9 - 19 10]
        IL_0008: ret
    
      } // end of method LazySingleton::.ctor
    
      .method public hidebysig specialname instance object
        get_Obj() cil managed
      {
        .maxstack 8
    
        // [21 36 - 21 41]
        IL_0000: ldsfld       object Singleton6.LazySingleton::m_obj
        IL_0005: ret
    
      } // end of method LazySingleton::get_Obj
    
      .method public hidebysig specialname instance void
        set_Obj(
          object 'value'
        ) cil managed
      {
        .maxstack 8
    
        // [21 50 - 21 63]
        IL_0000: ldarg.1      // 'value'
        IL_0001: stsfld       object Singleton6.LazySingleton::m_obj
        IL_0006: ret
    
      } // end of method LazySingleton::set_Obj
    
      .method public hidebysig static class Singleton6.LazySingleton
        GetLazySingleton() cil managed
      {
        .maxstack 2
        .locals init (
          [0] bool V_0,
          [1] bool V_1,
          [2] class Singleton6.LazySingleton V_2
        )
    
        // [24 9 - 24 10]
        IL_0000: nop
    
        // [25 13 - 25 35]
        IL_0001: volatile.
        IL_0003: ldsfld       class Singleton6.LazySingleton modreq ([System.Runtime]System.Runtime.CompilerServices.IsVolatile) Singleton6.LazySingleton::singleton
        IL_0008: ldnull
        IL_0009: ceq
        IL_000b: stloc.0      // V_0
    
        IL_000c: ldloc.0      // V_0
        IL_000d: brfalse.s    IL_0043
    
        // [26 13 - 26 14]
        IL_000f: nop
    
        // [27 17 - 27 38]
        IL_0010: ldsfld       object Singleton6.LazySingleton::m_obj
        IL_0015: call         void [System.Threading]System.Threading.Monitor::Enter(object)
        IL_001a: nop
    
        // [28 17 - 28 39]
        IL_001b: volatile.
        IL_001d: ldsfld       class Singleton6.LazySingleton modreq ([System.Runtime]System.Runtime.CompilerServices.IsVolatile) Singleton6.LazySingleton::singleton
        IL_0022: ldnull
        IL_0023: ceq
        IL_0025: stloc.1      // V_1
    
        IL_0026: ldloc.1      // V_1
        IL_0027: brfalse.s    IL_0037
    
        // [29 17 - 29 18]
        IL_0029: nop
    
        // [30 21 - 30 53]
        IL_002a: newobj       instance void Singleton6.LazySingleton::.ctor()
        IL_002f: volatile.
        IL_0031: stsfld       class Singleton6.LazySingleton modreq ([System.Runtime]System.Runtime.CompilerServices.IsVolatile) Singleton6.LazySingleton::singleton
    
        // [31 17 - 31 18]
        IL_0036: nop
    
        // [32 17 - 32 37]
        IL_0037: ldsfld       object Singleton6.LazySingleton::m_obj
        IL_003c: call         void [System.Threading]System.Threading.Monitor::Exit(object)
        IL_0041: nop
    
        // [33 13 - 33 14]
        IL_0042: nop
    
        // [34 13 - 34 30]
        IL_0043: volatile.
        IL_0045: ldsfld       class Singleton6.LazySingleton modreq ([System.Runtime]System.Runtime.CompilerServices.IsVolatile) Singleton6.LazySingleton::singleton
        IL_004a: stloc.2      // V_2
        IL_004b: br.s         IL_004d
    
        // [35 9 - 35 10]
        IL_004d: ldloc.2      // V_2
        IL_004e: ret
    
      } // end of method LazySingleton::GetLazySingleton
    
      .method private hidebysig static specialname rtspecialname void
        .cctor() cil managed
      {
        .maxstack 8
    
        // [12 9 - 12 45]
        IL_0000: newobj       instance void [System.Runtime]System.Object::.ctor()
        IL_0005: stsfld       object Singleton6.LazySingleton::m_obj
        IL_000a: ret
    
      } // end of method LazySingleton::.cctor
    
      .property instance object Obj()
      {
        .get instance object Singleton6.LazySingleton::get_Obj()
        .set instance void Singleton6.LazySingleton::set_Obj(object)
      } // end of property LazySingleton::Obj
    } // end of class Singleton6.LazySingleton
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154

    饿汉式

    开发人员把双检索技术捧的太高了,在不该使用它的地方仍然在使用它。大多数时候,这个技术实际上会损害效率。下面这中的要简单的多。

        public sealed class Singleton {
    
            private static Singleton s_value = new Singleton();
    
            private Singleton()
            {
    
            }
            public static Singleton GetSingleton() { return s_value; }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    由于代码首次访问类的成员的时,CLR会自动调用类型的类构造器,所以首次有一个线程查询Singleton的GetSingleton的方法的时候,CLR会自动调用类构造器。从而创建一个对象实例。此外,CLR已保证了类构造器的调用是线程安全的。

    非阻塞版本

    来自CLR via C#,代码如下:

        public sealed class Singleton {
    
            private static Singleton s_value = null;
    
            private Singleton()
            {
    
            }
    
            public Singleton GetSingleton() {
                if (s_value != null) return s_value;
    
                //创建一个新的单实例对象,先把它固定下来(如果另一个线程还没有固定它的话)
                Singleton temp = new Singleton();
                Interlocked.CompareExchange(ref s_value, temp, null);
    
                //如果这个线程竞争失败,新建的第二个实例对象会被垃圾回收
    
                //返回对单实例对象的引用
                return s_value;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    优势:速度快,永不阻塞线程。

    //Int32 old = location1;
    //if (location1 == comparand) location1 = value;
    //return old;
    public static Int32 CompareExchange(ref Int32 location1, Int32 value, Int comparand)
    
    • 1
    • 2
    • 3
    • 4

    FCL

    System.Lazy类封装了单例模式

    public class Lazy<T> {
    	public Lazy(FunC<T> valueFactory, LazyThreadSafeMode mode);
    	public Boolean IsValueCreated { get; }
    	public T value { get; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public static void Main(string[] args)
    {
    	  Console.WriteLine("Hello World!");
    	  Lazy<String> s = new Lazy<string>(() => DateTime.Now.ToLongTimeString(), true);
    	  Console.WriteLine(s.IsValueCreated);//好没有查询value 所以返回false
    	  Console.WriteLine(s.Value);//现在调用委托
    	  Console.WriteLine(s.IsValueCreated);// 已经查询了value 返回true
    	  Thread.Sleep(1000);
    	  Console.WriteLine(s.Value);//委托没有调用,显示相同的结果
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    内存有限时,可以使用System.Threading.LazyInitializer类的静态方法。

        class MainClass
        {
            public static void Main(string[] args)
            {
                string name = null;
                //由于name为null,所以委托执行
                LazyInitializer.EnsureInitialized(ref name, () => "Burning");
                Console.WriteLine(name);
                //由于name不为null,所以委托不运行,name不变
                LazyInitializer.EnsureInitialized(ref name, () => "无情剑客");
                Console.WriteLine(name);
    
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    最终运行结果:
    在这里插入图片描述

    参考

    https://www.jianshu.com/p/45885e50d1c4
    CLR via C#(第四版)
    https://www.cnblogs.com/xz816111/p/8470048.html

    写在最后

    这个算是补上8月分的,8月事情比较多。好久没写破解相关的文章了,接下来会写一些破解的文章,破解的目标是虎牙直播客户端。欢迎大家关注。

    公众号

    更多内容,欢迎关注我的微信公众号: 半夏之夜的无情剑客。
    在这里插入图片描述

  • 相关阅读:
    iMazing 3 for Windows iOS设备管理软件2024最新功能解析
    MapReduce
    基于OpenCV的轮廓检测(3)
    前缀和(区间和,子矩阵的和)
    bpftrace:简便输出调试信息
    从入门到精通:Java三目运算符详细教程!
    分享在大疆DJI(深圳总部)工作的日常和福利
    传感模块:MATEKSYS Optical Flow & LIDAR 3901-L0X
    pytest Allure报告生成全量配置git+jenkins+allure
    在前端html页面中向服务器发送post登录请求
  • 原文地址:https://blog.csdn.net/helloworlddm/article/details/126721269