bigywater.tech
Java · 179

安全发布:对象构造完成后如何被其它线程正确看到

对象构造完成后,怎样才算「安全发布」给其它线程?这篇把几种常见发布方式放进同样的场景:读线程在热循环里等对象出现,看哪些能看到、看到的对象是否完整(final 字段与内存序见前两篇)。

3 秒仍未看到
普通静态字段发布的对象,热循环里的读线程
约 207 ms
volatile / AtomicReference / synchronized / ConcurrentHashMap 发布后读线程看到对象
start / join / 类初始化
本身带有 happens-before 的发布方式

发布方式对比

实测 · 本机执行输出
plain:3 秒后读线程仍然没有看到发布的对象(循环没有结束)
volatile:循环在 207 ms 后结束,读到对象 a = 1,b = 2,items = [x, y]
atomic:循环在 209 ms 后结束,读到对象 a = 1,b = 2,items = [x, y]
synchronized:循环在 210 ms 后结束,读到对象 a = 1,b = 2,items = [x, y]
chm:循环在 204 ms 后结束,读到对象 a = 1,b = 2,items = [x, y]

自带 happens-before 的发布

实测 · 本机执行输出
start:Thread.start() 之前写入的普通字段,新线程里读到 plain.a = 1,items = [x, y]
join:被 join 的线程里写入的普通字段,join 返回后读到 plain.a = 7,items = [x, y]
holder:类初始化器里构造的 static final 对象,另一个线程第一次访问 Holder.INSTANCE 读到 a = 1,items = [x, y]

分步演示

安全发布
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12
    • 没有验证:未安全发布的对象在读线程看到时是否会处于半构造状态(上一篇的 jcstress 在本机没有测出)、双重检查锁定不加 volatile 的实际失败、BlockingQueue 与 Exchanger 的发布、不同 JIT 阶段下普通字段的表现;普通字段的结果只验证了热循环这一种场景
    京ICP备2026067502号-1京公网安备11010502063487号