AndroidKotlin之始Android知识

只一篇就够了·设计模式(5) - 单例模式

2016-11-23  本文已影响136人  Carltony

单例模式(Singleton Pattern)确保一个类只有一个实例,并且提供一个全局的访问。

单例模式随处可见,比如线程池缓存对话框日志对象等,这些时候如果制造出多个实例,程序运行就会出现预期之外的情况。
这里可能有疑问,我用全局静态变量也能做到一个类只有一个实例,为什么要引入这样一个设计模式呢?原因其实很简单,全局静态变量会造成资源浪费:假设这个类非常消耗资源,程序在运行过程中,不是每一次都用到这个类,那就是极大的浪费。

类图

类图不是目的,仅仅帮助理解

[图片上传失败...(image-1fb5c0-1527174223215)]

单例模式的类图很简单,只有一个类,有一个代表自己实例的instance变量,还有一个提供全局访问的静态方法getInstance()

以下的代码和思路是针对Java语言

单例类型

单例模式分为懒汉式和饿汉式,区别在于实例化单例对象的时机。

懒汉式

在懒汉式单例模式实现中,不管单例是否用到,都会实例化一个单例对象。典型的写法如下:

/**
 * 单例
 * Created by Carlton on 2016/11/21.
 */
class Singleton private constructor()
{
    companion object
    {
        private val instance = Singleton()
        fun instance() = instance
    }
}

因为Kotlin和Java在静态语法上的不一致,后面的代码都用Java来实现方便理解

/**
 * 单例模式
 * Created by Carlton on 2016/11/21.
 */
public class Singleton
{
    private Singleton()
    {
        
    }

    private static Singleton instance = new Singleton();
    public static Singleton instance()
    {
        return instance;
    }
}

饿汉式非常简单,也不会出现资源占用之外的其他问题,就不多说。

饱汉式、常规方法

饱汉式也就是常规实现方式比较复杂,原因是我们用到类实例的时候才会去实例化,这中间会出现各种各样的情况。

介绍了两种实现方式,接下来我们实现一个常规的单例模式:

/**
 * 单例模式
 * Created by Carlton on 2016/11/21.
 */
public class Singleton
{
    private Singleton()
    {

    }

    private static Singleton instance = null;
    public static Singleton instance()
    {
        if(instance == null)
        {
            // 1
            instance = new Singleton();
        }
        return instance;
    }
}

在客户端获取单例的时候,检查对象是否是null如果是,则实例化一个,如果不是则直接返回已有的对象,如果在单线程的情况下,确实如此,现在如果,两个或者两个以上的线程就有问题了:

怎么解决这个问题呢?不慌解决,先看看一下双重验证和volatile

双重检查和volatile

如果加上线程锁,好像问题就解决了,先看看加线程锁怎么写:

/**
 * 单例模式
 * Created by Carlton on 2016/11/21.
 */
public class Singleton
{
    private Singleton()
    {

    }

    private static Singleton instance = null;
    public static Singleton instance()
    {
        if(instance == null)
        {
            // 1
            synchronized (Singleton.class)
            {
                // 2
                if(instance == null)
                { // 3
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

现在看看多线程的情况程序会出现什么问题:

可能有人会觉得volatile可以解决问题,修改变量申明:

private static volatile Singleton instance = null;

先看看volatile是什么?

volatile变量具有synchronized的可见性特性,但是不具备原子特性。这就是说线程能够自动发现volatile变量的最新值。volatile变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。

通过这个描述知道volatile是一个轻量级的线程同步,之前出现的问题在于线程没有同步化的条件下读取instance,现在加上volatile问题就解决了。但是:JDK1.5之前,这样使用双重检查还是有问题。

Java中如何正确的实现单例模式

说了这么多,如何才能正确实现单例模式呢?

/**
 * 单例模式
 * Created by Carlton on 2016/11/21.
 */
public class Singleton
{
        private static volatile Singleton instance = null;
        private Singleton()
        {
        }
        public static synchronized Singleton instance()
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
        return instance;
        }
}

多说几句,如果把同步锁加到方法上面,代表这个方法同一时间只有一个线程能够进入方法,这个时候后面的线程进入就会正常的直接返回instance实例。

总结

单例模式在思路上是很简单的模式,也就不提供例子,单例模式还有很多单例模式的变种,但是核心没变:一个类只有一个实例;这个实例由自己来实例化;单例模式没有提供公共的构造函数,所以其他类不能对其实例化。需要注意的是,这个模式的复杂点在于实现方式,如何才能保证在各种情况下只有一个类实例才是关键点。

😊查看更多😊

不登高山,不知天之高也;不临深溪,不知地之厚也
感谢指点、交流、喜欢

上一篇 下一篇

猜你喜欢

热点阅读