
在 java 多线程环境下,即使对象本身不可变,其引用的发布仍需遵循安全发布原则;直接赋值静态引用(如 test.holder = new holder(42))不保证可见性,必须借助 volatile、final 字段初始化、静态初始化器或同步机制来确保其他线程能立即看到该引用。
《Java 并发编程实战》中明确指出:不可变对象(immutable objects)本身可经由任意方式发布,但关键在于——这里的“发布”指的是对象实例的构造完成且其状态对所有线程可见;而引用变量本身的可见性,仍受 Java 内存模型(JMM)约束。
以问题中的代码为例:
public class Test {
public static Holder holder = null; // 非 volatile,非 final
}
// 线程 A 执行:
Test.holder = new Holder(42);尽管 Holder 是不可变类(假设其所有字段为 final 且无修改逻辑),new Holder(42) 的构造过程在当前线程内是安全的(final 字段的正确初始化可被正确发布),但将该对象引用写入静态变量 holder 这一操作本身不具备 happens-before 关系。因此,其他线程可能看到 holder == null,或更危险地——看到一个部分构造的 Holder 引用(若 Holder 构造存在重排序且未正确防护)。
✅ 正确做法:使用 volatile 保障引用写入的可见性与禁止重排序:
立即学习“Java免费学习笔记(深入)”;
public class Test {
public static volatile Holder holder = null;
}
// 线程 A:
Test.holder = new Holder(42); // ✅ volatile 写入 → 对所有线程立即可见
// 线程 B 可安全读取:
if (Test.holder != null) {
int value = Test.holder.getValue(); // ✅ 一定能看到完整构造的 Holder(42)
}⚠️ 注意事项:
- 若 holder 在类加载时即完成初始化(静态初始化器),则天然安全发布:
public class Test { public static final Holder holder = new Holder(42); // ✅ 安全:类初始化由 JVM 保证同步 } - 仅靠对象不可变 不能免除引用发布的同步责任。不可变性解决的是“对象状态不会变”,而非“引用何时被看见”。
- synchronized 块、Lock、AtomicReference 等同样可实现安全发布,但 volatile 是最轻量且语义清晰的选择(适用于单次写入、多次读取场景)。
总结:不可变对象降低了并发复杂度,但绝非“免同步银弹”。安全发布 = 不可变对象 + 可见性保障机制(volatile / final / 同步)。忽视引用层面的内存可见性,仍会导致典型的竞态条件和数据不一致问题。










