最终字段 vs. 易变字段

huangapple go评论125阅读模式
英文:

Final fields Vs. Volatile

问题

java文档中关于final字段语义的说明:

在JDK14中的final字段

他们保证能够看到在构造函数中设置的final字段,请在下面找到代码:

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
        } 
    } 
}

现在我对volatilefinal有些困惑。

根据我的理解,final字段用于确保您无法在应用程序中更改变量,而volatile保证保持顺序并避免发生之前的关系。

那么我的问题是,他们如何通过使用final变量而不是volatile来保证可见性和顺序性呢?请帮忙解答。

英文:

From the java docs of final field semantics:

Final field in JDK14

They are guaranteeing to see the final field as set in constructor, please find the code below:

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
        } 
    } 
}

Now here I am confused about volatile vs final .

To my understanding final filed is used make sure you can not change the variable in application and volatile guarantees to maintain the order and avoid happen before relationship.

So my question is how come they guarantee the visibility and ordering using final variable instead of volatile ? Please help.

答案1

得分: 1

因为他们是这样定义的,现在虚拟机的工作是遵循这个定义。

当然,随后的问题是:为什么他们要这样定义呢?

这是因为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 在这里并没有改变任何内容。

这种行为对大多数开发者来说是意料之外的,可能会导致非常难以调试的错误。因此,为了在一定程度上减少这个问题,规范进行了调整,要求在实例变为公共实例之前必须初始化 final 字段。

以上是上述例子,其中 x 被声明为 final

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

> So my question is how come they guarantee the visibility and ordering using final variable

Because they defined it that way, and it's now the job of the VM to adhere to this definition.

Of course, the follow-up question is: Why did they define it that way?

It's because of a weird "feature" of the Java memory model. Consider this example

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

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

The static intitializer will be compiled into this (simplified pseudocode):

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

As you can see, the instance is made public before the constructor is executed, volatile doesn't change anything here.

This behavior is unexpected for most developer and can lead to very hard-to-debug bugs. Thus, to reduce the extend of this issue a bit, the specification was adjusted to require final fields to be initialized before the instance is made public.

Example from above, with x declared final.

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

huangapple
  • 本文由 发表于 2020年5月29日 17:07:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/62082360.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定