目录

一、前言

二、什么是单例模式

1、Singleton类

2、客户端

三、单例模式三要素

四、单例模式的应用

1、数据库连接池

2、配置信息

3、日志记录器

五、单例模式优缺点

1、优点

①、节约资源

②、全局访问

③、控制实例

2、缺点

①、扩展困难

②、耦合度高

六、单例模式实现

1、饿汉式

1.1、优点

1.2、缺点 

2、懒汉式

3、双重加锁机制

4、静态初始化

七、注意事项

八、总结


一、前言

在软件设计中,单例模式是一种常见的设计模式,用于确保一个类只有一个实例,并提供全局访问点。本文将深入探讨单例模式的概念、应用场景、实现方式以及优缺点,以便更好地理解和应用这一重要的设计模式。

二、什么是单例模式

单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局的访问点来获取这个唯一实例。这种模式在需要控制某个类的实例数量,以及在多个地方共享同一个实例时非常有用。UML如下:

【设计模式】单例模式:保障独一无二的对象实例_实例化

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

1、Singleton类

class Singleton
    {
        private static Singleton instance;

        //构造方法让其private,这就堵死了外界利用new创建此类型实例的可能
        private Singleton() { }
        public static Singleton GetInstance() //此方法是获得本类实例的唯一全局访问点
        {
            if (instance==null) //如果实例不存在,则new一个新实例,否则返回已有的实例
            {
                instance = new Singleton();
            }
            return instance;
        }
    }

2、客户端

static void Main(string[]args)
        {
            Singleton s1 = Singleton.GetInstance();
            Singleton s2 = Singleton.GetInstance();

            //比较两次实例化后对象的结果是实例相同
            if (s1==s2)
            {
                Console.WriteLine("两个对象是相同的实例");
            }
            Console.Read();
        }

运行上述程序,程序台输出了“两个对象是相同的实例”,这说明Singleton1和Singleton2是相同的实例,也即一个类仅有一个实例。

三、单例模式三要素

使用单例模式需有一下三点:

  • 要有一个自己的私有类型的变量
  • 要有一个私有的构造函数
  • 全局访问点(static)

四、单例模式的应用

1、数据库连接池

在应用中共享数据库连接池的实例,避免频繁地创建和释放连接。

2、配置信息

用于管理系统的配置信息,确保在不同地方使用相同的配置。

3、日志记录器

保证只有一个日志记录器实例,避免过多的日志产生。

五、单例模式优缺点

1、优点

①、节约资源

单例模式避免了多次创建相同实例的开销,节约了系统资源。

②、全局访问

单例模式提供全局的访问点,方便其他部分获取唯一实例。

③、控制实例

单例模式可以对类的实例数量进行控制,确保只有一个实例存在。

2、缺点

①、扩展困难

单例模式的设计通常是静态的,不易于扩展和继承。

②、耦合度高

单例模式会导致全局状态的存在,可能增加代码的耦合度

六、单例模式实现

1、饿汉式

// 饿汉式单例
public class Singleton1 {
 
    // 指向自己实例的私有静态引用,主动创建
    private static Singleton1 singleton1 = new Singleton1();
 
    // 私有的构造方法
    private Singleton1(){}
 
    // 以自己实例为返回值的静态的公有方法,静态工厂方法
    public static Singleton1 getSingleton1(){
        return singleton1;
    }
}

 我们知道,类加载的方式是按需加载,且加载一次。。因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用;而且,由于这个类在整个生命周期中只会被加载一次,因此只会创建一个实例,即能够充分保证单例。

1.1、优点

这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

1.2、缺点 

在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

2、懒汉式

// 懒汉式单例
public class Singleton2 {
 
    // 指向自己实例的私有静态引用
    private static Singleton2 singleton2;
 
    // 私有的构造方法
    private Singleton2(){}
 
    // 以自己实例为返回值的静态的公有方法,静态工厂方法
    public static Singleton2 getSingleton2(){
        // 被动创建,在真正需要使用时才去创建
        if (singleton2 == null) {
            singleton2 = new Singleton2();
        }
        return singleton2;
    }
}

 我们从懒汉式单例可以看到,单例实例被延迟加载,即只有在真正使用的时候才会实例化一个对象并交给自己的引用。

这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。

3、双重加锁机制

public class Singleton
    {
        private static Singleton instance;
        //程序运行时创建一个静态只读的进程辅助对象
        private static readonly object syncRoot = new object();
        private Singleton() { }
        public static Singleton GetInstance()
        {
            //先判断是否存在,不存在再加锁处理
            if (instance == null)
            {
                //在同一个时刻加了锁的那部分程序只有一个线程可以进入
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象。

使用双重检测同步延迟加载去创建单例的做法是一个非常优秀的做法,其不但保证了单例,而且切实提高了程序运行效率

4、静态初始化

//阻止发生派生,而派生可能会增加实例
    public sealed class Singleton
    {
        //在第一次引用类的任何成员时创建实例,公共语言运行库负责处理变量初始化
        private static readonly Singleton instance=new Singleton();
        
        private Singleton() { }
        public static Singleton GetInstance()
        {
            return instance;
        }
    }

 在实际应用中,C#与公共语言运行库也提供了一种“静态初始化”方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决多线程环境下它是不安全的问题。

七、注意事项

  • 单例类只能有一个实例
  • 单例类必须自己创建自己的唯一实例
  • 单例类必须给所有其他对象提供这一实例

八、总结

单例模式是一种重要的设计模式,它确保一个类只有一个实例,提供全局访问点来获取这个唯一实例。根据实际情况,可以选择不同的实现方式来满足需求。通过合理地应用单例模式,可以优化系统的性能和资源使用。