美文网首页AndroidKotlin之始Android知识
只一篇就够了·设计模式(5) - 单例模式

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

作者: Carltony | 来源:发表于2016-11-23 10:40 被阅读136次

单例模式(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如果是,则实例化一个,如果不是则直接返回已有的对象,如果在单线程的情况下,确实如此,现在如果,两个或者两个以上的线程就有问题了:

  • 如果两个线程都到1这个位置
  • 那么现在的情况就是if (instance == null)判断的时候两个线程都通过了
  • 这个时候instance会实例化两次,这两个线程拿到的不是同一个实例

怎么解决这个问题呢?不慌解决,先看看一下双重验证和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;
    }
}

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

  • 如果有两个线程都到了1
  • 因为同步锁的原因,只有一个线程可以先进入到2
  • 当第一个线程进入3实例化一个instance后,第二个线程进入判断的时候,就不会进入3
    这就是双重验证,在C/C++中,这样做是没有问题的,但是:双重检查对Java语言编译器不成立!原因在于,Java编译器中,Singleton类的初始化与instance变量赋值的顺序不可预料,如果一个线程在没有同步化的条件下读取instance引用,并调用这个对象的方法的话,可能会发现对象的初始化过程还没有完成,从而造成崩溃。

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

private static volatile Singleton instance = null;

先看看volatile是什么?

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

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

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

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

  • 使用饿汉式
  • JDK1.5以后使用带volatile修饰的双重检查
  • 同步锁加到方法上:
/**
 * 单例模式
 * 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实例。

总结

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

😊查看更多😊

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

相关文章

  • 单例模式 Singleton Pattern

    单例模式-菜鸟教程 iOS中的设计模式——单例(Singleton) iOS-单例模式写一次就够了 如何正确地写出...

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

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

  • 设计模式

    title: 设计模式date: 2016-08-19 15:38:44tags: 设计模式 单例模式 单例模式只...

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • 单例模式

    目录 1、设计模式简介 2、什么是单例模式 3、单例模式应用场合 4、单例模式作用 5、单例模式3个要点/要素 6...

  • 设计模式(Swift) - 3.观察者模式、建造者模式

    上一篇 设计模式(Swift) - 2.单例模式、备忘录模式和策略模式中讲了三种常见的设计模式. 单例模式: 限制...

  • python中OOP的单例

    目录 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 单例

    目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 设计模式系列--单例模式

    单例模式介绍 单例模式是设计模式中最常见也最简单的一种设计模式,单例模式的主要作用是保证在Java程序中,某个类只...

  • 设计模式 - 单例模式

    设计模式 - 单例模式 什么是单例模式 单例模式属于创建型模式,是设计模式中比较简单的模式。在单例模式中,单一的类...

网友评论

    本文标题:只一篇就够了·设计模式(5) - 单例模式

    本文链接:https://www.haomeiwen.com/subject/mkpupttx.html