You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

单线程写多线程读场景下,volatile是否始终必要?

问题分析:无volatile时子线程能否可靠读取变量更新值

你的判断存在潜在风险——虽然实际运行中大概率能读到1,但从Java内存模型(JMM)的规范角度,这段代码不保证子线程一定能读取到更新后的变量值,编译器和处理器的优化完全可能干扰最终结果。

核心原因:缺少Happens-Before关系

在没有volatile、锁或其他同步机制的情况下,主线程对completed的写入操作,和子线程对completed的读取操作之间没有JMM定义的happens-before关系。这意味着JMM不要求主线程的写操作结果必须对其他线程可见,哪怕代码的执行顺序看起来是“先写后读”。

可能的优化干扰场景

  • 编译器优化:编译器可能将completed的读写操作局限于寄存器,不及时刷回主存。比如主线程把completed=1写入寄存器后,没有同步到主存,子线程读取的就是主存里的初始值0;极端情况下,编译器甚至可能认为该写入对主线程无影响,直接将写操作优化掉。
  • 处理器指令重排与缓存一致性:处理器可能对内存访问指令进行重排,或者因为缓存行局部性优化,主线程的缓存更新没有及时同步到主存。哪怕子线程延迟5ms读取,只要没有触发缓存同步的机制(比如内存屏障),子线程的缓存依然可能保留旧值。

为什么实际运行可能“看起来正常”?

你测试时大概率能读到1,是因为Executors的线程池实现内部(比如schedule、shutdown、awaitTermination方法)可能包含了同步操作(比如锁、volatile变量读写),这些操作会自动触发内存屏障,间接把主线程对completed的写入同步到主存。但这是依赖线程池实现细节的侥幸行为,不是JMM的规范保证——一旦线程池实现变更、JVM版本更换,这种间接的内存屏障可能消失,代码就会出现读取旧值的问题。

结论

从Java并发规范的严谨性出发,必须给completed加上volatile关键字,才能可靠保证子线程读取到主线程更新后的变量值。不能依赖“延迟5ms”或第三方组件的内部同步来替代显式的可见性保证。

内容的提问来源于stack exchange,提问作者Jacky Wang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:35:10