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

Java中volatile字段是否能获得与final字段相同的初始化可见性保障?

问题解答

核心结论

  • final字段的构造初始化可见性保障不适用于volatile字段
  • 即使将示例中的y修改为volatile,依然有可能观测到j=0的结果

原因解释

JMM针对final字段做了专门的特殊规定:

当对象的构造函数执行完毕时,该对象被视为完全初始化。仅在对象完全初始化后才能看到该对象引用的线程,保证能看到该对象final字段的正确初始化值。

这个规则是final字段独有的,volatile字段没有对应的设计,它的可见性保障只针对自身读写操作的顺序,不涉及构造函数执行和对象引用发布的重排序限制。

你给出的示例场景如下:

class FinalFieldExample { 
    final int x;
    int y; 
    static FinalFieldExample f;

    public FinalFieldExample() {
        x = 3; 
        y = 4; 
    } 

    static void writer() {
        f = new FinalFieldExample();
    } 

    static void reader() {
        if (f != null) {
            int i = f.x;  // 保证一定能读到3
            int j = f.y;  // 可能读到0
        } 
    } 
}

这里的核心问题是静态变量f是普通字段,没有同步或volatile修饰,属于不安全的对象发布:JMM允许将f = new FinalFieldExample()的执行流程重排序为:

  1. 分配FinalFieldExample的内存空间,所有字段默认初始化为0(此时x=0,y=0)
  2. 将内存地址赋值给静态变量f,此时f已经不为null
  3. 执行构造函数内的字段赋值操作(x=3、y=4)

哪怕y被修改为volatile,也没有规则能阻止步骤2和步骤3的重排序,所以reader线程依然可能在步骤2执行完、步骤3还没执行的时候读取到f!=null,进而拿到y的默认初始值0。

如果要避免这个问题,你可以将静态变量f修饰为volatile,此时对f的volatile写会禁止构造操作重排到赋值给f之后,就能保证reader线程读到非null的f时,构造函数已经执行完成。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 23:36:03