
本文解析system.console()方法为何采用双重检查锁定(double-checked locking)模式,揭示其通过局部变量缓存、volatile字段协同与细粒度同步提升并发性能的设计意图。
System.console() 是 Java 6 引入的静态工具方法,用于安全、高效地获取 JVM 关联的唯一 Console 实例。其源码看似冗余的赋值逻辑——如 (c = cons) == null 和后续重复赋值——实则是经典并发优化模式的严谨体现:
public static Console console() {
Console c;
if ((c = cons) == null) { // 第一次检查(无锁)
synchronized (System.class) {
if ((c = cons) == null) { // 第二次检查(加锁后再次确认)
cons = c = SharedSecrets.getJavaIOAccess().console();
}
}
}
return c;
}该实现本质是双重检查锁定(Double-Checked Locking),核心目标是在保证线程安全的前提下,避免对高频调用方法施加全局同步开销。
关键设计点解析如下:
-
避免过度同步:若直接使用 synchronized static Console console(),每次调用均需获取 System.class 锁,即使 cons 已初始化完成。在高并发场景下,这将引发显著争用和性能下降。
立即学习“Java免费学习笔记(深入)”;
-
局部变量 c 的必要性:
- cons 字段被声明为 volatile(JDK 源码中可见),确保可见性但带来内存读取开销;
- 将 cons 赋值给局部变量 c 后,后续所有访问均操作栈上变量,既规避了多次 volatile 读,又防止在锁外判断后、返回前被其他线程修改 cons 导致不一致(即“指令重排序”或“可见性窗口”风险);
- 即使 cons 在 return c; 前被另一线程置为 null(极罕见,但理论上可能),返回的仍是已缓存的有效引用,保障结果稳定性。
volatile + DCL 的正确性前提:此模式自 Java 5 起因内存模型增强而变得可靠——volatile 写保证之前所有操作的完成与可见性,配合两次检查,可安全实现单例延迟初始化。
⚠️ 注意:双重检查锁定易出错,绝不建议自行在非标准场景下复刻。它依赖精确的 volatile 语义、无锁读—锁内写—局部缓存三者协同。现代开发更推荐使用 java.util.concurrent.ConcurrentHashMap 或 Holder 类静态内部类 等更安全、更易维护的延迟初始化方案。
总结而言,System.console() 的精巧实现并非代码“怪异”,而是 JDK 工程师在并发安全性、运行时性能与内存模型约束之间权衡后的典范实践——用少量可读性代价,换取生产环境下的确定性高性能。










