Singleton
定义
- 保证一个类仅由一个实例,并提供全局访问点。
隶属于创建型设计模式
分类
- 懒汉模式:延迟加载,只有在使用时才开始初始化
- 线程安全问题
- double check 加锁优化
- 编译器JIT、CPU有可能对指令进行重排,导致使用到尚未初始化的实例,可以通过关键字volatile进行修饰,防止指令重排序
- 饿汉模式: 初始化阶段就完成了实例的初始化,本质上是借助JVM类加载机制,保证实例的唯一性。
- 类加载过程:类的生命周期分为(装载、连接、初始化、使用、卸载),在连接阶段(验证、准备、解析三个部分)中的准备阶段主要是为static修饰的成员变量分配内存,并设置初始值。final修饰的会直接赋值(而不是使用默认值0/null)。除了final修饰的成员变量会在第三阶段将静态变量赋值,执行顺序时父类静态域和静态代码块,然后就是子类静态域或者子类静态代码块,然后是静态属性。
- 静态内部类实现
懒汉双检查示例代码
public class LazySingletonTest {public static void main(String[] args) {new Thread(() -> {LazySingleton instance = LazySingleton.getInstance();System.out.println(instance);}).start();new Thread(() -> {LazySingleton instance = LazySingleton.getInstance();System.out.println(instance);}).start();}}class LazySingleton {private static volatile LazySingleton instance; // volatile 禁止重排序private LazySingleton() {// 私有构造器}public static LazySingleton getInstance() {if (instance == null) {synchronized (LazySingleton.class) {if (instance == null) {// 字节码层面// 1.在堆中创建一块地址,并返回地址的引用// 2.invokespecial 对堆中地址进行初始化// 3.赋值instance = new LazySingleton();}}}return instance;}}
饿汉模式实例代码
public class HungrySingletonTest {public static void main(String[] args) {HungrySingleton instance = HungrySingleton.getInstance();HungrySingleton instance1 = HungrySingleton.getInstance();System.out.println(instance == instance1);}}class HungrySingleton {// 类加载机制private static HungrySingleton instance = new HungrySingleton();private HungrySingleton() {}public static HungrySingleton getInstance() {return instance;}}
使用场景
- 想确保任何情况下绝对只有一个实例(数据库连接池)
- 可以把工厂类设计成单例模式
- 单例模式与享元模式
优点: - 在内存中只有一个实例,减少了内存开销。
- 可以避免对资源的多重占用
- 设置全局访问点,严格控制访问
缺点: - 没有接口扩展困难
重点
- 私有构造器
- 线程安全
- 延迟加载
- 序列化和反序列化安全
- 反射
- 枚举单例
- 单例 Double Check
- 静态内部类 基于类初始化的延迟加载解决方案
