用supplier实现单例惰性加载的核心是将实例创建延迟到首次调用get()时执行,需配合static final声明和线程安全初始化策略(如静态内部类或双重检查锁)确保只创建一次,而非supplier接口本身保证单例。

用 Supplier 实现单例的惰性加载,核心是把“创建实例”的动作延迟到第一次调用时才执行,避免类加载或构造开销提前发生。它不是替代传统单例写法,而是提供一种更轻量、更函数式、更易测试的按需初始化方式。
Supplier 本身不保证单例,需配合外部控制
Supplier<t></t> 只是一个无参、返回 T 的函数式接口,每次调用 get() 都可能新建对象。要实现“只创建一次”,必须让同一个 Supplier 实例被复用,并在其内部封装线程安全的首次初始化逻辑。
- 不能直接写
new Supplier() { ... }每次都 new —— 这会失去惰性和唯一性 - 推荐将
Supplier声明为 static final 字段,确保全局唯一引用 - 内部用 双重检查锁(DCL) 或 静态内部类 保证懒加载 + 线程安全
推荐写法:静态内部类 + Supplier 封装
这是兼顾懒加载、线程安全与简洁性的主流方案:
public class LazySingleton {
private static class Holder {
static final LazySingleton INSTANCE = new LazySingleton();
}
private LazySingleton() {}
public static final Supplier<lazysingleton> SUPPLIER = () -> Holder.INSTANCE;
}
</lazysingleton>
使用时:LazySingleton.SUPPLIER.get() 第一次调用触发 Holder 类初始化,从而创建实例;后续调用直接返回缓存好的对象。JVM 保证类初始化是线程安全的,无需额外同步。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
更灵活的变体:带参数/工厂逻辑的 Supplier
如果单例依赖外部配置或需要定制化构建(比如传入数据库 URL),可用 Supplier 包裹一个闭包:
public class ConfigurableService {
private static volatile ConfigurableService instance;
private final String config;
private ConfigurableService(String config) {
this.config = config;
// 模拟耗时初始化
}
public static Supplier<configurableservice> supplierFor(String config) {
return () -> {
if (instance == null) {
synchronized (ConfigurableService.class) {
if (instance == null) {
instance = new ConfigurableService(config);
}
}
}
return instance;
};
}
}
</configurableservice>
调用:ConfigurableService.supplierFor("prod").get()。注意这里每次调用 supplierFor 都返回新 Supplier,所以适用于不同配置的独立单例场景;若需全局唯一,应把 supplier 提前生成并复用。
和 Spring @Lazy 或 volatile + DCL 的区别
Supplier 方式本质是把“获取实例”变成一个可传递、可组合、可延迟求值的动作,适合函数式链式调用或注入到其他组件中:
- 比
@Lazy更底层、不依赖框架,纯 Java SE 可用 - 比手写 volatile + DCL 更语义清晰:
get()明确表达“我要这个实例”,而非隐式字段访问 - 便于单元测试:可轻松 mock 或替换为测试用的
Supplier
不复杂但容易忽略:Supplier 是行为载体,单例保障靠的是它的复用性 + 内部初始化策略,不是接口本身。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










