Java中不可变对象引用发布的线程可见性问题咨询
首先明确《Java Concurrency in Practice》里3.5.5节这句话的核心语义:
不可变对象可通过任意机制发布;有效不可变对象必须安全发布。
这里的"任意机制发布"指的是不可变对象的内部状态不会因为发布方式不当而出现线程可见的不一致,但并不保证发布的引用本身对其他线程的可见性。针对你的代码场景,分两点说明:
1. 引用Test.holder的可见性不保证
Test.holder是普通静态变量,没有volatile修饰。当某线程执行Test.holder = new Holder(42);时,JVM不会强制插入写内存屏障,该线程本地缓存中的引用更新可能不会立即同步到主内存。其他线程可能一直读到旧的null值,或者在某个不确定的时间点才读到新值——完全没有可见性保证,和volatile变量的立即可见性完全不同。
2. 不可变Holder对象的状态是安全的
如果某个线程最终读到了Test.holder的新引用,那么它看到的Holder对象的状态一定是完整且正确的。这是因为不可变对象的所有字段都是final(假设你的Holder确实符合不可变定义:所有字段为final、无对外修改状态的方法、构造过程中没有逸出this引用),根据Java内存模型,final字段的初始化会在构造函数完成前同步到主内存,所以只要对象构造正确,即使引用是通过非安全发布的,一旦线程看到引用,就能看到对象的完整状态。
总结一下:不可变对象的"任意发布"只保证对象自身状态的安全性,不保证引用的可见性。如果需要让Test.holder的更新对其他线程立即可见,还是需要给它加上volatile修饰,或者用其他安全发布机制(比如静态初始化块、同步方法赋值等)。
内容的提问来源于stack exchange,提问作者yas

