单例模式学习

单例模式

概念
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有某个对象被创建;而且还提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

保证一个类只有一个实例:如果你创建了一个对象,同时过一会后决定再创建一个新对象,此时会获得之前已创建的对象,而不是一个新对象。注意:普通构造函数无法实现上述行为,因为构造函数的设计决定了它必须总是返回一个新对象

为该实例提供一个全局访问节点:和全局变量一样,单例模式也允许在程序的任何地方访问特定对象。但是它可以保护该实例不被其他代码覆盖。

优点

  • 只生成一个实例,减少了系统的性能开销和内存开支
    (目的可能为了控制某些共享资源(如数据库或文件)的访问权限
  • 可以避免对资源的多重占用
  • 可以在系统设置全局的访问点,优化和共享资源访问

缺点

  1. 单例模式一般没有接口,扩展困难,若要扩展,除了修改代码基本上没有第二种途径可以实现
  2. 若单例模式没有实现,是不能进行测试的
  3. 单例类的职责过重,在一定程度上违背了“单一职责原则”

结构类图
单例模式学习
创建方式

  1. 饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高。
  2. 懒汉式:类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
  3. 静态内部方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
  4. 枚举单例: 使用枚举实现单例模式。优点:实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞, 缺点没有延迟加载。
  5. 双重检测锁方式 (因为JVM本质重排序的原因,可能会初始化多次,不推荐使用)

如果不需要延迟加载单例,可以使用枚举或者饿汉式,相对来说枚举性好于饿汉式。
如果需要延迟加载,可以使用静态内部类或者懒汉式,相对来说静态内部类好于懒汉式。 最好使用饿汉式

实现

所有单例的实现都包含以下两个相同的步骤:

  • 将默认构造函数设为私有, 防止其他对象使用单例类的new运算符。
  • 新建一个静态构建方法作为构造函数。 该函数会 “偷偷” 调用私有构造函数来创建对象, 并将其保存在一个静态成员变量中。 此后所有对于该函数的调用都将返回这一缓存对象。

如果你的代码能够访问单例类, 那它就能调用单例类的静态方法。 无论何时调用该方法, 它总是会返回相同的对象。

一、懒汉式
synchronized
将整个 getInstance() 方法设为同步(synchronized)。不高效,因为任何时候只能有一个线程调用getInstance() 方法,而同步操作只需要在第一次创建单例实例对象时才需要。

public class Singleton{
    private static Singleton instance;
    // 加 synchronized 保证只有一个线程进入方法
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }

}

双重检验锁模式DCL
同步块加锁

public class Singleton{
    
    // 声明成 static volatile
    private static volatile Singleton instance;
    
    // 私有,使外界构造方法失效
    private Singleton(){
		
    }
    
    // 声明成 static
    public static Singleton getInstance(){
        // 非空时直接返回
        if(instance == null){
            synchronized(Singleton.class){
                // 防止生成多个实例
                if(instance == null){
                     instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

第一个if非空直接返回,减少synchronized浪费性能
第二个if防止生成多个实例

懒汉式 静态内部类

public class Singleton{
    // 静态内部类只会被加载一次,故 instance 单例且线程安全
    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }
    // getInstance()方法调用时才加载静态内部类
    public static final Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

二、饿汉式

  • static:第一次加载类到内存中时就会初始化
  • final :保持引用变量instance指向值不变
public class Singleton{
    // 饿汉式
    private static final Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
}

三、枚举

public class Singleton{
    /**
     *枚举类型是线程安全的,并且只会装载一次
     */
    public enum SingletonEnum{
        INSTANCE;
        private final Singleton instance;
        SingletonEnum(){
            instance = new Singleton();
        }
        private Singleton getInstance(){
            return instance;
        }
    }

    public static Singleton getInstance(){
        return SingletonEnum.INSTANCE.getInstance();
    }
}
上一篇:抽丝剥茧聊Kotlin协程之聊聊Job和SupervisorJob的区别


下一篇:Python - 魔法方法