• 设计模式-创建型-单例模式


    前言:

      单例模式,顾名思义,只存在一个实例。官方定义:对于类的单例模式设计,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

      单例模式在写法上有很多种,有饿汉式(类加载的时候实例化),懒汉式(类在使用的时候实例化),保证线程安全的写法等。具体如下:(删除线表示不推荐使用)

        ① 饿汉式(静态常量) 

        ② 饿汉式(静态代码块)

        ③ 懒汉式(线程不安全)

        ④ 懒汉式(线程安全,同步方法)

        ⑤ 懒汉式(线程安全,同步方法)

        ⑥ 双重检查double check

        ⑦ 静态内部类

        ⑧ 枚举

      在IOC模式中,通常使用生命周期来实现单例,如services.AddSingleton<MyClass>();

    正文:

    ★饿汉式(静态常量)

     1 internal class Program
     2 {
     3     private static void Main(string[] args)
     4     {
     5         // 测试
     6         Singleton instance1 = Singleton.getInstance();
     7         Singleton instance2 = Singleton.getInstance();
     8 
     9         Console.WriteLine(instance1 == instance2);
    10         Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}");
    11         Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}");
    12     }
    13 }
    14 
    15 internal class Singleton
    16 {
    17     /// <summary>
    18     /// 1、私有化构造函数,外部不能new
    19     /// </summary>
    20     private Singleton()
    21     {
    22     }
    23 
    24     // 2、本类的内部创建对象实例
    25     private readonly static Singleton instance = new Singleton();
    26 
    27     // 3、提供一个公有的静态方法,返回对象实例
    28     public static Singleton getInstance()
    29     {
    30         return instance;
    31     }
    32 }
    view code

      优缺点说明:

        1、优点:写法简单,就是在类状态的时候就完成了实例化,避免线程同步问题。

        2、缺点:在类装载的时候完成实例化,没有达到lazy loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

        3、这种方式基于class loader机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,但是导致类装载的原因有很多,因此不能确定有其他的方式导致装载,这时候初始化instance就没有达到lazy loading的效果。

      结论:这种单例模式可用,可能造成内存浪费。

    ★饿汉式(静态代码块)

     1 internal class Program
     2 {
     3     private static void Main(string[] args)
     4     {
     5         // 测试
     6         Singleton instance1 = Singleton.GetInstance;
     7         Singleton instance2 = Singleton.GetInstance;
     8 
     9         Console.WriteLine(instance1 == instance2);
    10         Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}");
    11         Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}");
    12     }
    13 }
    14 
    15 internal class Singleton
    16 {
    17     /// <summary>
    18     /// 1、私有化构造函数,外部不能new
    19     /// </summary>
    20     private Singleton()
    21     {
    22     }
    23 
    24     public static Singleton GetInstance { get; private set; } = new Singleton();
    25 }
    view code

    ★懒汉式(线程不安全)

     1 internal class Program
     2 {
     3     private static void Main(string[] args)
     4     {
     5         // 测试
     6         Singleton1 instance1 = Singleton1.getInstance();
     7         Singleton1 instance2 = Singleton1.getInstance();
     8 
     9         Console.WriteLine(instance1 == instance2);
    10         Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}");
    11         Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}");
    12     }
    13 }
    14 
    15 internal class Singleton1
    16 {
    17     // 私有的静态变量
    18     private static Singleton1 instance;
    19 
    20     /// <summary>
    21     /// 1、私有化构造函数,外部不能new
    22     /// </summary>
    23     private Singleton1()
    24     {
    25     }
    26 
    27     /// <summary>
    28     /// 提供一个静态的公有方法,当使用到该方法时,才去创建instance,即懒汉式
    29     /// </summary>
    30     /// <returns></returns>
    31     public static Singleton1 getInstance()
    32     {
    33         if (instance == null)
    34         {
    35             instance = new Singleton1();
    36         }
    37         return instance;
    38     }
    39 }
    view code

      优缺点说明:

        1、起到了lazy loading效果,但是只能在单线程下使用。

        2、若在多线程下,一个线程进入if(singleton==null)判断语句块,还未来得及往下执行,另一个线程也通过了该判断,就会产生多个实例。所以在多线程环境下不可使用这种方式。

      结论:在实际开发中,不要使用这种方式。

    ★懒汉式(线程安全,同步方法) 

     1 internal class Program
     2 {
     3     private static void Main(string[] args)
     4     {
     5         // 测试
     6         Singleton3 instance1 = Singleton3.getInstance();
     7         Singleton3 instance2 = Singleton3.getInstance();
     8 
     9         Console.WriteLine(instance1 == instance2);
    10         Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}");
    11         Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}");
    12     }
    13 }
    14 
    15 internal class Singleton3
    16 {
    17     // 定义一个静态变量来保存类的实例
    18     private static Singleton3 instance;
    19 
    20     // 定义一个标识确保线程同步
    21     private static readonly object locker = new object();
    22 
    23     /// <summary>
    24     /// 私有化构造函数,外部不能new
    25     /// </summary>
    26     private Singleton3()
    27     {
    28     }
    29 
    30     /// <summary>
    31     /// 提供一个静态的公有方法,当使用到该方法时,才去创建instance,即懒汉式
    32     /// </summary>
    33     /// <returns></returns>
    34     public static Singleton3 getInstance()
    35     {
    36         // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
    37         // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
    38         // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
    39         lock (locker)
    40         {
    41             if (instance == null)
    42             {
    43                 instance = new Singleton3();
    44             }
    45         }
    46         return instance;
    47     }
    48 }
    view code

      优缺点说明:

        1、解决了线程不安全的问题。

        2、效率太低,每个线程都想获取类实例的时候,执行getInstance()时都需要进行同步,而该方法只需要执行一次实例化代码就够了,后面想获取该类的实例直接return就可以了。

      结论:在实际开发中,不推荐使用这种方式。    

    ★懒汉式(线程安全,同步代码块) 

     1 internal class Program
     2 {
     3     private static void Main(string[] args)
     4     {
     5         // 测试
     6         Singleton4 instance1 = Singleton4.getInstance();
     7         Singleton4 instance2 = Singleton4.getInstance();
     8 
     9         Console.WriteLine(instance1 == instance2);
    10         Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}");
    11         Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}");
    12     }
    13 }
    14 
    15 internal class Singleton4
    16 {
    17     // 定义一个静态变量来保存类的实例
    18     private static Singleton4 instance;
    19 
    20     // 定义一个标识确保线程同步
    21     private static readonly object locker = new object();
    22 
    23     /// <summary>
    24     /// 私有化构造函数,外部不能new
    25     /// </summary>
    26     private Singleton4()
    27     {
    28     }
    29 
    30     /// <summary>
    31     /// 提供一个静态的公有方法,当使用到该方法时,才去创建instance,即懒汉式
    32     /// </summary>
    33     /// <returns></returns>
    34     public static Singleton4 getInstance()
    35     {
    36         if (instance == null)
    37         {
    38             lock (locker)
    39             {
    40                 instance = new Singleton4();
    41             }
    42         }
    43         return instance;
    44     }
    45 }
    view code 

      优缺点说明:

        1、这种方式本意上时对上述方式的改进,改为同步产生实例化的代码块。

        2、但是这种方式没有解决线程安全问题。  

      结论:强烈不推荐。

    ★双重检查 

     1 internal class Program
     2 {
     3     private static void Main(string[] args)
     4     {
     5         // 测试
     6         Singleton5 instance1 = Singleton5.getInstance();
     7         Singleton5 instance2 = Singleton5.getInstance();
     8 
     9         Console.WriteLine(instance1 == instance2);
    10         Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}");
    11         Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}");
    12     }
    13 }
    14 
    15 internal class Singleton5
    16 {
    17     // 定义一个静态变量来保存类的实例
    18     private static Singleton5 instance;
    19 
    20     // 定义一个标识确保线程同步
    21     private static readonly object locker = new object();
    22 
    23     /// <summary>
    24     /// 私有化构造函数,外部不能new
    25     /// </summary>
    26     private Singleton5()
    27     {
    28     }
    29 
    30     /// <summary>
    31     /// 提供一个静态的公有方法,当使用到该方法时,才去创建instance,即懒汉式
    32     /// </summary>
    33     /// <returns></returns>
    34     public static Singleton5 getInstance()
    35     {
    36         // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
    37         // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
    38         // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
    39         // 双重锁定只需要一句判断就可以了
    40         if (instance == null)
    41         {
    42             lock (locker)
    43             {
    44                 if (instance == null)
    45                 {
    46                     instance = new Singleton5();
    47                 }
    48             }
    49         }
    50         return instance;
    51     }
    52 }
    view code

      结论:推荐使用。 

    ★静态内部类 

     1 internal class Program
     2 {
     3     private static void Main(string[] args)
     4     {
     5         // 测试
     6         Singleton2 instance1 = Singleton2.getInstance();
     7         Singleton2 instance2 = Singleton2.getInstance();
     8 
     9         Console.WriteLine(instance1 == instance2);
    10         Console.WriteLine($"instance1.GetHashCode={instance1.GetHashCode()}");
    11         Console.WriteLine($"instance2.GetHashCode={instance2.GetHashCode()}");
    12     }
    13 }
    14 
    15 internal class Singleton2
    16 {
    17     /// <summary>
    18     /// 1、私有化构造函数,外部不能new
    19     /// </summary>
    20     private Singleton2()
    21     {
    22     }
    23 
    24     private static class SingletonInstance
    25     {
    26         public readonly static Singleton2 INSTANCE = new Singleton2();
    27     }
    28 
    29     /// <summary>
    30     /// 提供一个静态的公有方法,当使用到该方法时,才去创建instance,即懒汉式
    31     /// </summary>
    32     /// <returns></returns>
    33     public static Singleton2 getInstance()
    34     {
    35         return SingletonInstance.INSTANCE;
    36     }
    37 }
    view code

      优缺点说明:

        1、这种方式采用了类装载的机制来保证初始化实例时只有一个线程。

        2、静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,通过getInstance方法才会装载SingletonInstance类,从而完成Singleton实例化。

        3、类的静态属性只会在类第一次装载的时候初始化。

        4、避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。

      结论:推荐使用。 

    ★枚举

       

      在java中提供该方式。 

    C#中实现了单例模式的类:

     1 // 该类不是一个公开类
     2 // 但是该类的实现应用了单例模式
     3 internal sealed class SR
     4 {
     5     private static SR loader;
     6     internal SR()
     7     {
     8     }
     9     // 主要是因为该类不是公有,所以这个全部访问点也定义为私有的了
    10      // 但是思想还是用到了单例模式的思想的
    11      private static SR GetLoader()
    12     {
    13          if (loader == null)
    14          {
    15              SR sr = new SR();
    16              Interlocked.CompareExchange<SR>(ref loader, sr, null);
    17          }
    18         return loader;
    19     }
    20 
    21     // 这个公有方法中调用了GetLoader方法的
    22     public static object GetObject(string name)
    23     {
    24          SR loader = GetLoader();
    25          if (loader == null)
    26          {
    27                return null;
    28          }
    29          return loader.resources.GetObject(name, Culture);
    30     }
    31 }
    view code

    参考:https://www.cnblogs.com/zhili/p/SingletonPatterm.html

  • 相关阅读:
    _MainTex_TexelSize
    资源处理参考
    unity 判断一个trans在不在sceen内
    DX11 绘制三角形 判断顺时针
    int型转LPCWSTR在MessageBox上显示
    sizeof struct
    buffer和cache
    DX11 三维空间: depth信息与stencil信息
    DX11 纹理的添加
    hlsl SV_POSITION
  • 原文地址:https://www.cnblogs.com/az4215/p/11509875.html
Copyright © 2020-2023  润新知