解惑:单例模式中饿汉式与懒汉式初始化的时机差异
饿汉式与懒汉式单例的初始化时机差异解析
你觉得两者没有差异的误区,核心在于没搞清楚Java类加载的触发时机不止调用getInstance()这一种情况,这正是两类单例初始化时机的本质区别所在。
1. 饿汉式(EagerSingleton)的初始化逻辑
饿汉式里的静态实例变量是在类加载阶段就完成初始化的:
public class EagerSingleton { // 类加载时立即初始化实例 private static final EagerSingleton instance = new EagerSingleton(); private EagerSingleton() {} public static EagerSingleton getInstance() { return instance; } }
根据Java的类加载机制,只要触发类加载的场景出现,这个instance就会被创建,常见的触发场景包括:
- 首次调用该类的任意静态方法(比如
getInstance()) - 访问该类的任意静态字段(哪怕不是
instance) - 通过
EagerSingleton.class引用类对象(比如反射、类型判断) - 初始化该类的子类(如果存在)
也就是说,哪怕你还没调用getInstance(),只要类被加载到JVM中,单例实例就已经存在于内存里了。
2. 懒汉式(LazySingleton)的初始化逻辑
懒汉式的静态实例变量仅声明不初始化,真正的实例创建被延迟到首次调用getInstance()时:
public class LazySingleton { // 仅声明静态变量,不立即初始化 private static LazySingleton instance; private LazySingleton() {} // 首次调用时才检查并创建实例 public static synchronized LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
哪怕类已经被加载(比如通过上述的类加载触发场景),只要没调用getInstance(),instance就始终是null,不会执行实例化逻辑。只有当第一次调用getInstance()时,才会完成实例的创建,后续调用直接返回已创建的实例。
3. 实际场景中的差异影响
- 启动速度:如果单例类初始化需要消耗大量资源(比如加载配置、连接数据库),饿汉式会在程序启动阶段就占用资源,可能拖慢启动速度;懒汉式则把初始化延迟到真正需要的时候,能优化启动性能。
- 线程安全:饿汉式依赖JVM类加载的线程安全性(类加载过程由JVM保证线程安全),天生不存在多线程问题;懒汉式则需要通过
synchronized(或双重检查锁等优化方式)来避免多线程下创建多个实例的问题。
内容的提问来源于stack exchange,提问作者Yu Xing
相关产品推荐
相关产品推荐

