简单的单例模式

Posted zhangxianshen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单的单例模式相关的知识,希望对你有一定的参考价值。

简单的单例模式

单例模式是现在最常用的软件设计模式,也是最简单设计模式,通过单例模式可以保证系统中,这个类只有一个实例

假如在下面这种情况下,我们有一个数据库连接类,在平常的时候,我们实例化这个类然后写入连接,第一次我链接了SQLServer数据库,第二次我链接了mysql数据库,但是我同时想用这两个数据库,发现不太合适,就需要一个方法方式来解决问题,这是最适合用单例模式了

虽然我举的栗子可能不太合适,但是大概就是这么一个意思,保证只有一个实例,避免多个实例不知道使用哪个或者多个实例导致报错

使用

单例模式要求类能够返回一个对象的引用和一个获得该实例的方法,静态的方法

实现主要是有两个步骤:

①:经该类的构造函数定义为私有的(private)

②:在这个类里提供一个静态方法

在这里要区别的是单例模式有两种:恶汉模式和懒汉模式

恶汉模式是在项目运行起来就实例化了这个类

懒汉模式是在需要这个类的时候才实例化这个类

先不多说了,上代码:

这个是恶汉模式:

/// <summary>
/// 单例模式的实现
/// </summary>
public class SingletonEh
{
    // 定义一个静态变量来保存类的实例
    private static SingletonEh uniqueInstance=new SingletonEh();

    private SingletonEh()
    {
    }

    /// <summary>
    /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
    /// </summary>
    /// <returns></returns>
    public static SingletonEh GetInstance()
    {

        return uniqueInstance;
    }
}

这个是不安全的懒汉模式:

/// <summary>
/// 单例模式线程不安全的实现(懒汉模式)
/// </summary>
public class SingletonThread
{
    // 定义一个静态变量来保存类的实例
    private static SingletonThread uniqueInstance;

    // 定义一个标识确保线程同步
    private static readonly object locker = new object();

    // 定义私有构造函数,使外界不能创建该类实例
    private SingletonThread()
    {
    }

    /// <summary>
    /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
    /// </summary>
    /// <returns></returns>
    public static SingletonThread GetInstance()
    {
                // 如果类的实例不存在则创建,否则直接返回
                if (uniqueInstance == null)
                {
                    uniqueInstance = new SingletonThread();
                }



        return uniqueInstance;
    }
}

大家可能注意到了我说的是不安全的懒汉模式,别急,这就来解释,在懒汉模式中,如果单线程的话没问题,如果是多线程呢,加入同时两个线程需要使用这个类,通过代码可以看到,都通过判断,都实例化了这个类,那就没有单例模式的意义了,既然有问题就有解决办法,下面这个是线程安全的单例模式,加锁的单例模式

/// <summary>
/// 单例模式线程安全的实现(懒汉模式)
/// </summary>
public class SingletonThread
{
    // 定义一个静态变量来保存类的实例
    private static SingletonThread uniqueInstance;

    // 定义一个标识确保线程同步
    private static readonly object locker = new object();

    // 定义私有构造函数,使外界不能创建该类实例
    private SingletonThread()
    {
    }

    /// <summary>
    /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
    /// </summary>
    /// <returns></returns>
    public static SingletonThread GetInstance()
    {
        // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
        // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
        // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"

            lock (locker)
            {
                // 如果类的实例不存在则创建,否则直接返回
                if (uniqueInstance == null)
                {
                    uniqueInstance = new SingletonThread();
                }
            }
        return uniqueInstance;
    }
}

这很安全了,但是,每次使用我都得等上一个任务完成解锁后才可以继续,这就造成了可能排老长的队,也不太合适,那么,咱们继续优化一下

/// <summary>
/// 优化后的单例模式线程安全的实现(懒汉模式)
/// </summary>
public class SingletonThreadOptimize
{
    // 定义一个静态变量来保存类的实例
    private static SingletonThreadOptimize uniqueInstance;

    // 定义一个标识确保线程同步
    private static readonly object locker = new object();

    // 定义私有构造函数,使外界不能创建该类实例
    private SingletonThreadOptimize()
    {
    }

    /// <summary>
    /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
    /// </summary>
    /// <returns></returns>
    public static SingletonThreadOptimize GetInstance()
    {

        // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
        // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
        // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
        // 双重锁定只需要一句判断就可以了
        if (uniqueInstance == null)
        {
            lock (locker)
            {
                // 如果类的实例不存在则创建,否则直接返回
                if (uniqueInstance == null)
                {
                    uniqueInstance = new SingletonThreadOptimize();
                }
            }
        }
        return uniqueInstance;
    }
}

优点

系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。

缺点

当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候

以上是关于简单的单例模式的主要内容,如果未能解决你的问题,请参考以下文章

性能比较好的单例写法

面试官所认为的单例模式

简单的单例模式其实也不简单

简单的单例模式其实也不简单!

你真的会写JAVA的单例模式吗?

片段作为 Android 中的单例