美文网首页
二、单例模式

二、单例模式

作者: topshi | 来源:发表于2021-10-10 23:55 被阅读0次

单例设计模式是最简单的一种创建型设计模式,其提供了创建对象的最佳实现。该模式涉及到一个单一的类,且该类负责创建自己的对象,同时确保只有一个对象被创建。该类提供了一种访问其唯一对象的方式,访问方式的实现是单例设计模式的核心,涉及到线程安全等问题。

  • 为何使用单例模式?
    通常来说,我们获取到一个对象实例,当只使用其内部提供的方法,而不关注其成员变量时,可以使用单例模式。
    • 节省内存空间:单例在内存中只有一个实例对象,节省内存空间,避免大量创建及销毁
    • 高性能:减少高质量资源重复占用,可以进行全部访问

单例模式理论

单例模式

  • 什么时候使用单例模式?
    • 重复对象需要频繁实例化及销毁的对象
    • 有状态化的工具对象
    • 频繁访问数据库或文件的对象

单例角色:单例模式只有一个单例角色,在单例的内部生成一个对象实例,同时提供一个方法用于获取这个对象实例。为了避免外界直接创建对象实例从而破坏单例模式,通常构造函数都是设置为私有的,外部只能通过方法获取到唯一的单例对象。

单例模式的实现方式

比较常见的单例实现方式有如下:

  • 饿汉式
  • 懒汉式
  • 静态内部类

代码实现

  • 饿汉式
package singleton.hungry;

/**
 * 单例模式:饿汉式实现方式,类在加载的时候就创建单例对象
 */
public class HungrySingleton {
    /**
     * 定义类静态变量,类加载的时候就已经创建好单例对象
     */
    private static final HungrySingleton INSTANCE = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return INSTANCE;
    }
}

饿汉式在类加载的时候就已经将单例对象创建好了,但是如果没有使用它,一定程度上造成浪费。不过,由于在类加载的时候单例对象就创建好了,因此它是线程安全的。

  • 懒汉式
package singleton.lazy;


/**
 * 单例模式:懒汉式实现方式
 */
public class LazySingleton {
    private static LazySingleton INSTANCE = null;

    private LazySingleton(){}

    public static LazySingleton getInstance(){
        if (INSTANCE == null){
            INSTANCE = new LazySingleton();
        }
        return INSTANCE;
    }
}

懒汉式是在外部调用类的方法获取单例对象时才会创建单例对象,它不会想饿汉式那样造成浪费。但是,会导致线程安全问题。在高并发情况下,可能多个线程都调用了构造方法来创建对象。为了解决这个线程并发问题,通常需要加锁。

  • 线程安全版懒汉式
package singleton.safeLazy;

/**
 * 单例模式:线程安全的懒汉式实现
 */
public class SafeLazySingleton {

    /**
     * 加volatile关键字,防止重排序
     */
    private static volatile SafeLazySingleton INSTANCE = null;
    
    private SafeLazySingleton(){}
    
    public static SafeLazySingleton getInstance(){
        if(INSTANCE == null){
            synchronized (SafeLazySingleton.class){
                // 双重校验
                if(INSTANCE == null){
                    INSTANCE = new SafeLazySingleton();
                }
            }
        }
        return INSTANCE;
    }
}

线程安全版的懒汉式实现要点是:1. volatile关键字 2. synchronized锁 3. 双重校验判空
1. volatile关键字可以防止初始化对象时由于编译器的作用导致指令重排序

一个对象初始化的步骤包括:

  • 给对象实例分配一块内存空间instance = allocate(SafeLazySingleton.class)
  • 针对分配好的内存空间的对象实例,执行构造方法,对这个对象实例进行初始化操作,对各个成员变量赋值,执行初始化逻辑。invokeConstructor(instance)
  • 经过上面两个步骤,一个对象实例完成;此时将instance指针指向这块内存空间,赋值给我们引用类型的变量,让它指向SafeLazySingleton对象的内存地址。

以上是正常的步骤,然而在编译器重排序的作用下,可能真正执行的指令顺序是 1-> 3-> 2。
所以当A线程去创建单例对象实例,进行到3步骤,由于重排序,此时的对象是残缺的;而此时B线程判断INSTANCE对象为空,于是进行了某些操作,由于是残缺对象,因此会出错。
2. 双重校验
第二重校验是避免A、B两个线程依次进入了同步代码块,重复创建单例对象。
3. synchronized同步锁
其实synchronized可以直接加在方法上,这样就不用双重校验了,但是这样的锁粒度比较大。

  • 静态内部类
package singleton.staticClass;

/**
 * 单例模式:静态内部类
 */
public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton(){}

    /**
     * 通过静态内部类加载时创建单例对象
     */
    private static class Inner{
        final static StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
    }

    /**
     * 第一次调用此方法时,触发静态内部类加载从而创建单例对象
     * @return
     */
    public static StaticInnerClassSingleton getInstance(){
        return Inner.INSTANCE;
    }
}

此方法利用了JVM类加载的线程安全实现单例。

相关文章

  • 单例模式

    一、单例模式介绍 二、单例模式代码实例

  • 单例模式

    一、实现单例模式 或者 二、透明的单例模式 三、用代理实现单例模式 四、JavaScript中的单例模式 在Jav...

  • OC - 单例模式

    导读: 一、什么是单例模式 二、单例的作用 三、常见的单例类 四、自定义单例类的方法 一、什么是单例模式 单例模式...

  • 设计模式-iOS常见

    一、单例模式 系统的单例模式(Singleton Pattern) 二、中介者模式 中介者模式(Mediator ...

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

  • 单例模式

    一、定义 单例模式:单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。 二、单例模式结构...

  • Python 面向对象7: 单例模式

    一、内容 1.1、单例设计模式 1.2、__new__方法 1.3、Python 中的单例 二、单例设计模式 2....

  • 单例模式

    一、介绍 二、单例模式代码实现 三、单例的简介写法

  • Swift单例模式

    1.第一种单例模式 2.第二种单例模式

网友评论

      本文标题:二、单例模式

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