单例模式
单例模式:
私有构造函数+提供一个方法或者变量让外部可以获取实例
饿汉式实现
枚举
静态成员变量实例初始化
提供方法返回成员变量(实例化的类)
懒汉(需要时加载)
在方法中直接返回
在方法中返回子类的静态成员变量(实例化好的class)
synchronized(class) 双重检测, 判断是否有实例了 + 获取类锁
注意:
枚举类型的单例,因为反序列化会检测是不是enum,是就会抛异常,所以不用担心反序列化问题且天然单例。
双重检测可能有空指针问题,因为指令重排序,可能返回一个不完整的对象,所以可以用 volatile 修饰变量。
推荐内部类实现的懒汉模式。
静态变量只会加载一次,但是是否完整就是不一定了。可能在没初始化完,就已经给变量赋予地址了。
代码案例
枚举单例 特点: 天然线程安全,防反射、防反序列化破坏单例,实现简单
饿汉式单例(静态成员变量实例初始化) 特点:类加载时就创建实例,线程安全(类加载天然同步),可能造成“类加载即创建”,有些场景会浪费资源
懒汉式(非线程安全,最简单版,不推荐) 特点:按需加载(第一次用到时才创建),多线程环境会有并发问题(可能创建多个实例)
懒汉式 + synchronized 整个方法(简单但性能一般) 特点:在方法上加 synchronized,保证线程安全,实现简单,每次调用都要获取锁,性能一般
懒汉式 - 双重检查锁(DCL,需 volatile) 特点(对应你笔记里的“synchronized(class) 双重检测 + volatile”): 兼顾“按需加载 + 多线程性能”,实现稍复杂,但非常常见,instance 必须用 volatile 修饰,以防指令重排序返回“半初始化对象”
静态内部类实现懒汉模式 特点:
利用类加载机制实现“按需加载 + 线程安全”
外部类加载时不会立即创建实例,只有第一次调用 getInstance() 时,才加载内部类并创建实例
性能好,写法优雅,是懒汉模式中非常推荐的一种
总结
推荐优先使用:枚举单例、静态内部类单例
想兼顾懒加载和性能:静态内部类 > DCL
只在单线程或测试/demo 用:最简单的懒汉式(不加锁)
Last updated