最终字段语义

Final Field Semantics

17.5。 final Field Semantics

示例 17.5-1。 Java 内存模型中的最终字段

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

我被这个问题困扰好几天了

谁能直接回答我为什么f.y可以看到0?

如果线程A赋值y:

Thread A:
   writer();

线程 B 读取 y:

Thread B
   reader();

然后 y 在没有任何同步的情况下被读取,因此可能看不到分配的值。这是 Java 同步要求的简单应用。如果还是不明白请在问题中说明。

换句话说,如果y被声明为volatile那么它就可以保证被看到。

class FinalFieldExample { 
    final int x;
    volatile int y; 

然后:

static void reader() {
    if (f != null) {
        int i = f.x;  // guaranteed to see 3  
        int j = f.y;  // guaranteed to see 4

这是因为写操作[this.]y=4和读操作f.y之间没有happens-before关系。分配 y=4 happens-before 构造函数完成,并且构造函数在分配给静态字段之前完成,但是由于两个线程都直接访问静态字段并执行没有直接的联合排序关系,没有正式保证写入non-final字段y在读取线程中可见。 JLS 确实 做出了关于最终字段的具体 承诺;本质上,有一个 happens-before 规则没有明确表达(可能应该)。

任何创建 happens-before 关系的机制都可以解决这个理论问题,包括使 either yf volatile.

(请注意,我非常怀疑是否存在任何实现会重新排序,以便在构造函数完成之前执行对 f 的赋值,但 JLS 指出没有 保证 除非在最终字段的情况下不会发生这种情况。)