
本文解析system.console()方法中看似冗余的局部变量赋值与双重检查锁定(double-checked locking)设计,揭示其在保证线程安全前提下避免同步开销的关键优化逻辑。
System.console() 是 Java 6 引入的静态工具方法,用于安全、高效地获取 JVM 关联的唯一 Console 实例。其源码中看似“绕弯”的写法实为经典并发优化实践:
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)模式的应用,核心目标是:在多线程环境下仅初始化一次 cons,同时避免每次调用都承受同步锁的性能开销。
为什么不能直接使用 synchronized 方法?
若改写为:
public static synchronized Console console() {
if (cons == null) {
cons = SharedSecrets.getJavaIOAccess().console();
}
return cons;
}虽然语义正确且线程安全,但会导致每次调用(无论是否已初始化)都阻塞并竞争 System.class 锁——在高并发场景下显著拖慢性能。而实际应用中,console() 多数调用发生在初始化之后,此时应尽可能“零成本”返回。
双重检查如何解决这个问题?
立即学习“Java免费学习笔记(深入)”;
- 第一次检查(外层 if):无锁读取 cons。若已初始化,直接返回,无任何同步开销;
- 仅当未初始化时,才进入 synchronized 块执行第二次检查与初始化;
-
局部变量 c 的关键作用:
- 避免多次读取 volatile 字段 cons(cons 被声明为 volatile,以确保可见性,但每次读取比普通字段更昂贵);
- 确保返回值稳定性:即使其他线程在返回前修改了 cons,局部变量 c 仍持有初始化时的确切引用,防止竞态导致返回 null 或不一致对象。
⚠️ 注意:此模式在 JDK 5+ 中才真正安全,前提是 cons 字段被声明为 volatile(实际源码中确为 private static volatile Console cons;)。volatile 保证了写操作的可见性与禁止指令重排序,使双重检查锁定得以成立。
总结而言,这段代码并非“奇怪”,而是兼顾线程安全性、初始化原子性与运行时性能的精巧设计——它代表了 Java 平台级 API 在底层并发控制上的成熟权衡。开发者在实现单例或延迟初始化逻辑时,可借鉴此模式,但务必配合 volatile 修饰符,并充分理解其内存模型约束。










