【问题标题】:Mixing volatile and non-volatile混合挥发性和非挥发性
【发布时间】:2019-11-25 16:56:37
【问题描述】:

我的问题适用于最初为null 的字段,然后初始化为非null 值,然后不再更改。

由于该字段需要尽快对所有线程可用,我需要使用volatile

但如果我想尽可能避免volatile 访问的开销(即当非volatile 字段就足够时),以下代码是否有意义?

public class User {
  private String nonVolatileName;
  private volatile String volatileName;

  public String getName() {
    final String name = nonVolatileName;
    return name != null ? name : volatileName;
  }

  private void initName() {
    volatileName = nonVolatileName = expensiveComputation();
  }

  …
}

【问题讨论】:

  • 你的 getName() 方法必须同步,这也是一个开销
  • @LeoLeontev 我不明白为什么必须同步getName
  • "name != null ?"也是开销。你确定 volatile 的开销更大吗? (我认为一些聪明的基准应该能够证明这一点)。
  • @Suma 我同意,我需要运行基准测试。

标签: java multithreading volatile


【解决方案1】:

是的,代码会起作用,因为“对易失性字段的写入(第 8.3.1.4 节)发生在每次后续读取该字段之前。” [1]

return name != null ? name : volatileName;

只要name == null,您将强制从 volatile 变量读取,直到它不为空(即 expensiveComputation 已完成),这在语义发生之前确保您在 @987654327 中看到非空值@从那时起。参见此处 [2]

尽管向你的同事解释这一点很有趣

[1]https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5-320

[2]Volatile variables and other variables

【讨论】:

  • 感谢您的全面回答!在 Java 中有更简单的方法吗?
  • 这很丑不是吗?您真的有这样的用例,使用/不使用volatile 会产生明显的不同吗?
  • 好吧,从智力上看,它似乎可以有所作为,因为该字段被非常频繁地访问;但正如 Suma 所建议的,我需要运行一个基准测试来证明它(或不证明它)。
  • 如果您有时间,请在此处发布基准测试结果作为评论,我很想知道您会看到什么。
  • @Suma 基准测试在这里:github.com/julienroyer/mixing-volatile-and-non-volatile - 这可能是错误的,因为我是基准测试工具的绝对初学者。我得到的结果(见结果目录)表明差异并不明显。
猜你喜欢
  • 1970-01-01
  • 2015-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-26
  • 1970-01-01
  • 1970-01-01
  • 2016-07-01
相关资源
最近更新 更多