【问题标题】:Java final & Safe PublicationJava最终和安全发布
【发布时间】:2018-04-24 16:33:38
【问题描述】:

当我读到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 的线程看到 f.x 的值 3,因为它是最终的。不能保证看到 y 的值 4,因为它不是最终值。

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

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

【问题讨论】:

  • thread executing reader may see f is not null, but the constructor of the object witch f referenced is not finished yet 这不可能发生,赋值发生在构造函数运行完成并且没有抛出异常之后。
  • 哪种机制保证赋值的发生发生在构造函数运行完成之后?In Jeremy Manson's blog,他说编译器转换可以改变代码周围,使Helper构造函数中的代码在写入之后发生到辅助变量。我认为jsr-133-faq中的代码和Jeremy Manson博客中的代码是同一场景。

标签: java final safe-publication


【解决方案1】:

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

初始化安全性仅保证值的可见性 在构造函数时可以通过最终字段访问 完成。对于通过非最终字段可访问的值,或 构建后可能会发生变化,必须使用同步来确保 可见性。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 2020-07-09
    • 1970-01-01
    • 2013-11-14
    • 1970-01-01
    相关资源
    最近更新 更多