Java 26 带来了一个重要的预览特性:JEP 526 Lazy Constants(惰性常量)。它由 JDK 25 中的 JEP 502 Stable Values 演进而来,旨在为单例模式和延迟初始化提供一种安全、高效且简洁的语言级支持。

延迟初始化的传统困境
在 Java 开发中,应用启动性能与运行时性能常需权衡。例如,大型应用启动时初始化大量未立即使用的组件会拖慢启动速度。
class Application {
static final OrderController ORDERS = new OrderController();
static final ProductRepository PRODUCTS = new ProductRepository();
// ... 更多组件
}
传统解决方案是延迟初始化,但这引入了新问题:
class OrderController {
private Logger logger = null;
Logger getLogger() {
if (logger == null) { // 非线程安全
logger = Logger.create(OrderController.class);
}
return logger;
}
}
- 线程安全问题:多线程环境下可能创建多个实例。
- 性能损失:失去
final 字段的常量折叠优化。
- 代码复杂:需要显式判空,增加维护成本。
传统的单例模式(如双重检查锁定)虽能解决线程安全,但代码冗长且无法充分利用 final 优化。JEP 526 Lazy Constants 正是为了解决这些痛点而生。
从JEP 502到JEP 526:一次重要的API重塑
JDK 25 的 Stable Values (预览)
在 JDK 25 中首次引入 StableValue API 作为预览特性。
// JDK 25
class OrderController {
private final StableValue<Logger> logger = StableValue.of();
Logger getLogger() {
return logger.orElseSet(() ->
Logger.create(OrderController.class));
}
}
社区反馈指出其 API 层级偏低、命名不够直观、使用稍显繁琐。
JDK 26 的 Lazy Constants (第二次预览)
JDK 26 基于反馈进行了重大革新,核心变化可概括为“做减法”:
- API 核心重命名:从
StableValue 改为 LazyConstant,语义更清晰。
// JDK 26
LazyConstant<Logger> logger = LazyConstant.of(() ->
Logger.create(OrderController.class));
- 移除底层操作:移除了
orElseSet()、setOrThrow() 等易引发竞态条件的方法,强制在构造时提供初始化函数,将线程安全完全封装。
- 集合 API 深度集成:相关工厂方法迁移至标准集合接口,更符合开发者习惯。
// JDK 26
List.ofLazy(size, creator)
Map.ofLazy(keySet, creator) // 新增支持
- 禁止 null 值:初始化函数禁止返回
null,简化实现并提升性能。
Lazy Constants 的核心优势
1. 线程安全的单例初始化
class Application {
static final LazyConstant<OrderController> ORDERS =
LazyConstant.of(OrderController::new);
public static OrderController orders() {
return ORDERS.get(); // 线程安全,仅初始化一次
}
}
get() 方法保证“恰好一次”初始化。
- 无需
synchronized 或 volatile。
- 高并发场景下依然安全。
2. 享受 JVM 常量折叠优化
当 LazyConstant 存储在 static final 字段中并被初始化后,JIT 可将其内容视为真正常量,进行内联展开、死码消除等优化,效果类似于内部的 @Stable 注解,但现在是安全、公开的 API。
3. 构建延迟初始化链
class OrderController {
private final LazyConstant<Logger> logger =
LazyConstant.of(() -> Logger.create(OrderController.class));
void submitOrder() {
logger.get().info("Order submitted"); // 首次使用时初始化
}
}
整个依赖链均可按需初始化,极大改善大型应用启动性能。
基于Lazy Constants的五种单例模式新实践
1. 静态字段(推荐)
public class DatabaseService {
private static final LazyConstant<DatabaseService> INSTANCE =
LazyConstant.of(DatabaseService::new);
public static DatabaseService getInstance() {
return INSTANCE.get();
}
private DatabaseService() {
// 昂贵的初始化操作
connectToDatabase();
}
}
优点:简洁、线程安全、性能最优。
2. 枚举单例增强版
public enum ConfigManager {
INSTANCE;
private final LazyConstant<Config> config =
LazyConstant.of(() -> loadConfigFromFile());
public Config getConfig() {
return config.get();
}
}
优点:天然防止反射攻击,配置延迟加载。
3. 带参数的单例池
public class CacheService {
private final String cacheName;
private static final Map<String, LazyConstant<CacheService>> INSTANCES =
Map.ofLazy(Set.of("user", "product", "order"),
name -> new CacheService(name));
public static CacheService getInstance(String name) {
return INSTANCES.get(name).get();
}
}
优点:利用 Map.ofLazy() 便捷管理多个单例。
4. 资源池化
class ConnectionPool {
static final List<LazyConstant<Connection>> POOL =
List.ofLazy(10, _ -> createConnection());
public static Connection getConnection() {
long index = Thread.currentThread().threadId() % 10;
return POOL.get((int)index).get();
}
}
适用场景:数据库连接池、线程池等资源管理。
5. ServiceLoader 集成
public class MyServiceProvider implements MyService {
private static final LazyConstant<MyServiceProvider> INSTANCE =
LazyConstant.of(MyServiceProvider::new);
// ServiceLoader 会调用此方法
public static MyServiceProvider provider() {
return INSTANCE.get();
}
}
适用场景:服务提供者接口(SPI)实现。
性能对比
| 实现方式 |
首次调用开销 |
后续调用开销 |
线程安全 |
常量折叠 |
代码复杂度 |
| 饿汉式 |
无 |
最低 |
安全 |
支持 |
低 |
| 双重检查锁定 |
中等 |
低 |
安全 |
不支持 |
高 |
| 静态内部类 |
无 |
最低 |
安全 |
支持 |
中 |
| LazyConstants |
极低 |
最低 |
安全 |
支持 |
极低 |
根据 OpenJDK 微基准测试,相比 volatile 双重检查锁定,LazyConstants 在后续调用上性能提升约 15-30%。对于启动阶段未使用的组件,可节省 100% 的初始化时间。
使用注意事项
-
启用预览特性:
# 编译
javac --release 26 --enable-preview Main.java
# 运行
java --enable-preview Main
-
必须使用 final 字段:只有 static final 字段才能触发 JVM 常量折叠优化。
// 正确
static final LazyConstant<Config> CONFIG = LazyConstant.of(...);
// 错误(失去优化)
static LazyConstant<Config> config = LazyConstant.of(...);
-
避免在初始化函数中抛出异常:建议在函数内部处理异常并返回默认值,以保证单例的可用性。
-
注意反射限制:实例 final 字段仍可能被反射修改(这会破坏优化),但 static final 字段是相对安全的。
总结与展望
JEP 526 Lazy Constants 不仅是一次 API 重命名,更是一次重要的设计重塑。它提供了语义更清晰、使用更安全、体验更友好的延迟常量支持。对于 Java 开发者而言,这意味着能够以一行代码简洁地实现线程安全的单例和延迟初始化,提升应用启动速度,并降低代码维护成本。
未来,我们可能看到该特性成为稳定特性,并与 Project Valhalla 的值类型等更深度的 JVM 优化集成。建议开发者关注此特性,在未来项目中尝试使用 LazyConstants 替代传统的单例实现,以享受现代 Java 带来的性能与简洁。
