首页 > 解决方案 > Java最终和安全发布

问题描述

当我阅读jsr-133-faq时,有疑问“最终字段如何在新 JMM 下工作?”,它说:

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;
      int j = f.y;
    }
  }
}

上面的类是如何使用 final 字段的示例。保证执行 reader 的线程看到 fx 的值 3,因为它是最终的。不能保证看到 y 的值 4,因为它不是最终值。

这让我很困惑,因为 writer 中的代码不是安全发布的,线程执行 reader 可能会看到 f 不是 null,但是 f 引用的对象的构造函数还没有完成,所以即使 x 是 final,线程执行 reader不能保证看到 fx 的值 3

这是我很困惑的地方,如果我错了,请纠正我,非常感谢。

标签: javafinalsafe-publication

解决方案


这就是重点,这就是 JMM 中 final 字段的优点。是的,编译器通常可以在对象完全构造之前分配对对象的引用,这是不安全的发布,因为可以在部分构造状态下访问对象。但是,对于final字段,JMM(和编译器)保证将引用分配给对象之前首先准备好所有 final 字段。发布可能仍然不安全,并且当新线程访问它时,对象仍然只是部分构造,但至少最终字段将处于其预期状态。来自 Java Concurrency in Practice 的第 16.3 章:

初始化安全性仅对构造函数完成时可通过 final 字段访问的值提供可见性保证。对于可通过非最终字段访问的值,或构造后可能更改的值,您必须使用同步来确保可见性。

我还建议阅读第 16.3 章了解更多详细信息。


推荐阅读