决赛场易挥发的

时间:2020-05-29 09:07:35

标签: java final

来自最终java语义的field文档:

Final field in JDK14

他们保证会看到final中设置的constructor字段,请找到以下代码:

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;  // guaranteed to see 3  
            int j = f.y;  // could see 0
        } 
    } 
}

现在,我对volatilefinal感到困惑。

据我了解,使用final字段时请确保您无法在应用程序中更改变量,并且volatile保证维持顺序并避免在关系发生之前发生。

所以我的问题是,它们如何使用final变量而不是volatile来保证可见性和排序?请帮忙。

1 个答案:

答案 0 :(得分:1)

所以我的问题是,如何使用最终变量保证可见性和顺序

因为他们是用这种方式定义的,所以现在,遵循此定义是VM的工作。

当然,后续问题是:为什么他们用这种方式定义它?

这是由于Java内存模型的怪异“功能”所致。考虑这个例子

class Foo {
    int x;
    volatile int y;
    Foo() {
        x = 3;
        y = 4;
    }

    static final Foo INSTANCE;
    static {
       INSTANCE= new Foo();
    }
}

静态初始化器将被编译为以下代码(简化的伪代码):

   Foo tmp = allocate(Foo.class)
   Foo.INSTANCE = tmp
   tmp.x = 3
   tmp.y = 4

如您所见,该实例在执行构造函数之前已公开,volatile在此不做任何更改。

对于大多数开发人员而言,此行为是意外的,并且可能导致非常难以调试的错误。因此,为了稍微减少此问题的范围,对规范进行了调整,以要求在实例公开之前初始化最终字段。

上面的示例,其中x被声明为final

   Foo tmp = allocate(Foo.class)
   tmp.x = 3
   Foo.INSTANCE = tmp
   tmp.y = 4