鲲鹏社区首页
中文
注册
我要评分
文档获取效率
文档正确性
内容完整性
文档易理解
在线提单
论坛求助

final域

使用final域标记的变量,编译器和CPU需要遵守两个重排序规则:

构造函数内对一个final域的写入,与随后把该被构造对象的引用赋值给一个引用变量,这两个操作不能重排序。

初次读一个包含final域的对象的引用,与随后初次读这个final域, 这两个操作不能重排序。

写入示例如下:

public class FinalDemo {
    int i;          // 普通变量
    final int j;    // final 变量
    
    static FinalDemo obj;
    
    public FinalDemo {  // 构造函数
        i = 1;          // 写普通域
        j = 2;          // 写 final 域
    }
    
    public static void write() {    // 写线程 A 执行
        obj = new FinalDemo();
    }
    
    public static void read() { // 读线程 B 执行
        FinalDemo object = obj; // 读对象引用
        int a = object.i;       // 读普通域
        int b = object.j;       // 读 final 域
    }
}

下图是一种上述代码的执行时序:

在图中,写普通域的操作被编译器重排序到构造函数之外,线程B读到了i未初始化的值。而写final域的操作,被写final域的重排序规则“限定”在了构造函数之内,线程B能正确读到final变量初始后的值。

在图中,读对象的普通域被重排序到读对象引用之前。读普通域时,该域还没被写线程A写入,这是一个错误的读取操作。而final域的读操作会被“限定”在对象引用之后,此时final域已经被正确初始化。