懒汉式和饿汉式区别:单例模式中的两种实现方式
在开发中,单例模式是使用频率很高的一种设计模式。它保证一个类只有一个实例,并提供全局访问点。而在实现单例时,最常见的两种写法就是“懒汉式”和“饿汉式”。虽然它们目标一致,但在加载时机、线程安全和资源利用上却有明显差别。
饿汉式:提前加载,用空间换时间
饿汉式就像一个做事特别积极的人,不管后面用不用得上,一开始就先把事情办了。在类加载的时候就创建好实例,之后每次获取都是同一个对象。
这种方式实现简单,天然线程安全,因为类加载机制保证了实例只会被初始化一次。
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return instance;
}
}但问题也在这儿——如果这个实例占用资源多,而程序整个运行周期都没怎么用到它,那就白白浪费了内存。就像你一进办公室就把所有灯都打开,结果只在工位上干活,其他地方压根没人去。
懒汉式:按需加载,延迟初始化
懒汉式更像一个讲究效率的人,不到真正需要时不做事。实例的创建被推迟到第一次调用 getInstance() 方法时才进行。
这样可以节省内存,尤其适合那些重量级对象或者启动阶段不需要立即使用的场景。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}不过这种写法在多线程环境下会有问题。如果多个线程同时判断 instance == null,可能就会创建出多个实例,破坏单例原则。
线程安全的懒汉式:双重检查锁定
为了解决线程安全问题,常见的做法是加上同步控制。最高效的方式是“双重检查锁定”(Double-Checked Locking),既保证性能又确保安全。
public class ThreadSafeLazySingleton {
private static volatile ThreadSafeLazySingleton instance;
private ThreadSafeLazySingleton() {}
public static ThreadSafeLazySingleton getInstance() {
if (instance == null) {
synchronized (ThreadSafeLazySingleton.class) {
if (instance == null) {
instance = new ThreadSafeLazySingleton();
}
}
}
return instance;
}
}这里的 volatile 关键字很重要,它防止指令重排序,确保其他线程看到的是完整构造的对象。
选择哪种方式?看实际场景
如果你的对象初始化成本不高,或者确定一定会用到,那饿汉式更简单可靠。它在类加载时完成初始化,没有并发风险,代码也干净。
但如果你在意启动速度和内存占用,而且这个实例不是每次都用得上,那就选懒汉式,配合双重检查锁定,做到延迟加载又线程安全。
比如做某个后台服务模块,配置管理器只需要在用户请求特定功能时才加载,这时候懒汉式就很合适。而像日志记录器这种从启动就开始用的组件,饿汉式反而更稳妥。