• 单例模式


      单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    一、单例模式

      通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

      单例模式结构图:

      

      Singleton类,定义一个GetInstance操作,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例。

      单例模式示例:

    namespace ConsoleApplication1
    {
        public class Singleton
        {
            private static Singleton instance;
    
            private Singleton()     //将构造方法设为private,防止外界利用new创建实例。
            { 
                
            }
    
            public static Singleton GetInstance()   //此方法用于获得本类实例的唯一全局访问点
            {
                if (instance == null)           //若实例不存在则new一个新实例,否则返回已有实例
                {
                    instance = new Singleton();
                }
                return instance;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Singleton singleton1 = Singleton.GetInstance();
                Singleton singleton2 = Singleton.GetInstance();       //调用静态方法获得实例
                if (singleton1 == singleton2)
                {
                    Console.WriteLine("两个对象是相同的实例!");       //输出 两个对象是相同的示例
                }
                Console.ReadKey();
            }
        }  
    }

      单例模式与实用类(静态方法)的不同点:

        1、实用类不保存状态,仅提供一些静态方法或静态属性让你用,而单例是由状态的。

        2、实用类不能用于继承多态,而单例虽然实例唯一,却是可以有子类来继承。

        3、实用类只不过是一些方法属性的集合,而单例却是有着唯一的对象实例。

    二、多线程时的单例

      在上面的例子中,如果是在多线程的程序中,多个线程同时(注意是同时)访问Singleton类,调用GetInstance()方法,会有可能造成创建多个实例的。因此可以通过加lock来处理,lock是确保当一个线程位于代码的临界区,另一个线程不进入临界区。如果其他线程视图进入锁定的代码,则它将一直等待(即被阻止),直到对象被释放。

      双重锁定代码示例:

    namespace ConsoleApplication1
    {
        public class Singleton
        {
            private static Singleton instance;
            private static readonly object syncRoot = new object();
    
            private Singleton()     //将构造方法设为private,防止外界利用new创建实例。
            { 
                
            }
    
            public static Singleton GetInstance()   //此方法用于获得本类实例的唯一全局访问点
            {
                if (instance == null)           //如果实例为空,在外面做一层判断的作用是,当实例不为null时,不用lock,减少资源消耗。
                {   
                    lock (syncRoot)             //锁定
                    {
                        if (instance == null)   //当两个线程同时调用GetInstance()时,他们都可以通过第一重instance=null的判断,然后由于lock机制,
                        //这两个线程只有一个进入,另一个等候,如果没有第二重的instance=null的判断,则第一个线程创建了实例,第二个线程由于过了第一重instance=null,
                        //因此在没有第二重instance == null判断的情况下还是会创建实例。
                        {
                            instance = new Singleton();
                        }
                    }
                }
                return instance;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Singleton singleton1 = Singleton.GetInstance();
                Singleton singleton2 = Singleton.GetInstance();       //调用静态方法获得实例
                if (singleton1 == singleton2)
                {
                    Console.WriteLine("两个对象是相同的实例!");       //输出 两个对象是相同的示例
                }
                Console.ReadKey();
            }
        }  
    }

    三、静态初始化

      其实在实际应用中,C#与公共语言运行库也提供了一种‘静态初始化’方法,这种方法不需要开发人员显式地编写线程安全代码,既可解决多线程环境下它是不安全的问题。相对于上面的来说,两种方法都可以达到相同的单例目的,只是实现更加简单而已,来看下实现代码:

    namespace ConsoleApplication1
    {
        public sealed class Singleton   //注意必须设置为密封类
        {
            private static readonly Singleton instance = new Singleton();    //注意声明为只读
            private Singleton() 
            {
    
            }
            public static Singleton GetInstance()
            {
                return instance;
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Singleton singleton1 = Singleton.GetInstance();
                Singleton singleton2 = Singleton.GetInstance();       //调用静态方法获得实例
                if (singleton1 == singleton2)
                {
                    Console.WriteLine("两个对象是相同的实例!");       //输出 两个对象是相同的示例
                }
                Console.ReadKey();
            }
        }  
    }

      这种实现与前面的示例类似,也是解决了单例模式试图解决的两个基本问题:全局访问和实例化控制,公共静态属性为访问实例提供了一个全局访问点。不同之处在于它依赖公共语言运行库来初始化变量。由于构造方法是私有的,因此不能在类本身以外实例化Singleton类;因此,变量引用的是可以再系统中存在的唯一实例。不过要注意,instance变量标记为readonly,这意味着只能在静态初始化期间或在类的构造函数中分配变量。由于这种静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汉单例模式。而上面的第二种是在第一次被引用的时候,才会将自己实例化,所以就被称之为懒汉单例模式

      由于饿汉式,即静态初始化的方式,它是类一加载就实例化对象,所以要提前占用系统资源。然而懒汉式,又会面临着多线程访问的安全性问题,需要做双重锁定这样的处理才可以保证安全。所以到底使用哪一种方式取决于实际的需求。从C#语言的角度来讲,饿汉式的单例模式已经足够满足我们的需求。

  • 相关阅读:
    赋值运算符函数
    系统设计面试题分析
    内核线程和用户线程的区别
    线程和进程区别
    TCP程序设计
    UDP程序设计
    死锁,死锁必要条件及处理策略
    Linux进程同步机制
    Windows与Linux下进程间通信技术比较
    Windows下进程通信方式
  • 原文地址:https://www.cnblogs.com/qixuejia/p/4370265.html
Copyright © 2020-2023  润新知