【问题标题】:How does Java manage the visibility of a volatile field?Java 如何管理 volatile 字段的可见性?
【发布时间】:2014-10-05 16:50:01
【问题描述】:

这个 Q 正在寻找有关 Java 如何使 volatile 字段可见的具体细节。

Java 中的volatile 关键字用于在对变量执行写操作后立即使该变量的读取器“主动”可见。这是一种先发生关系的形式——使写入的结果暴露给访问该变量的该内存位置以供某些用途的任何人。并且当使用时,使对该变量的读/写操作具有原子性——对于longdouble 也是如此——对所有其他 var 类型的 R/W 已经是原子的。

我正在寻找 Java 在写操作后如何使变量值可见?

例如:以下代码来自this讨论的答案之一:

public class Foo extends Thread {
  private volatile boolean close = false;
  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

对布尔文字的读写是原子的。如果调用了上面的方法close(),即使没有声明为volatile,将close的值设置为true也是一个原子操作。

volatile 在此代码中所做的更多工作是确保在更改此值时立即看到它。

volatile 究竟是如何实现这一目标的?

通过优先处理对 volatile 变量进行操作的线程?如果是这样-如何在线程调度中,或者通过使具有读取操作的线程去查找一个标志以查看是否有写入线程挂起?我知道“对易失性字段的写入发生在每次后续读取同一字段之前。”是否在线程中进行选择,即在将 CPU 时间分配给只读线程之前对 volatile 变量进行写操作的线程?

如果这是在线程调度级别进行管理的(我对此表示怀疑),那么运行一个在 volatile 字段上写入的线程会产生比看起来更大的效果。

Java 究竟如何管理volatile 变量的可见性?

TIA。

【问题讨论】:

  • 它保证写入 volatile 变量的线程将写入主内存,而不是写入处理器缓存,并且读取 volatile 变量值的线程不会从处理器读取它缓存,但来自主内存。我不是这些低级硬件机制的专家,所以我会让专家回答,但你应该明白。与日程安排或优先级无关。
  • CPU 完成所有工作。 JVM 确保它使用正确的指令。

标签: java multithreading volatile happens-before


【解决方案1】:

这是OpenJDK源码中关于volatile的注释

// ----------------------------------------------------------------------------
// Volatile variables demand their effects be made known to all CPU's
// in order.  Store buffers on most chips allow reads & writes to
// reorder; the JMM's ReadAfterWrite.java test fails in -Xint mode
// without some kind of memory barrier (i.e., it's not sufficient that
// the interpreter does not reorder volatile references, the hardware
// also must not reorder them).
//
// According to the new Java Memory Model (JMM):
// (1) All volatiles are serialized wrt to each other.  ALSO reads &
//     writes act as aquire & release, so:
// (2) A read cannot let unrelated NON-volatile memory refs that
//     happen after the read float up to before the read.  It's OK for
//     non-volatile memory refs that happen before the volatile read to
//     float down below it.
// (3) Similar a volatile write cannot let unrelated NON-volatile
//     memory refs that happen BEFORE the write float down to after the
//     write.  It's OK for non-volatile memory refs that happen after the
//     volatile write to float up before it.
//
// We only put in barriers around volatile refs (they are expensive),
// not _between_ memory refs (that would require us to track the
// flavor of the previous memory refs).  Requirements (2) and (3)
// require some barriers before volatile stores and after volatile
// loads.  

希望对你有帮助。

【讨论】:

  • 你从哪里得到这个
  • @Roam,我使用OpenJDK 的来源和来自hotspot's 解释器的评论随时纠正我的英语
  • 他们通常会在此处提供源链接。如果不是秘密或任何东西,请传递它
  • @Roam,这不是秘密 :) 文件的最新版本位于 here。注释从第 2023 行开始。
【解决方案2】:

据此:

http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile

Java 的新内存模型是这样做的

1) 禁止编译器和运行时在寄存器中分配 volatile 变量。

2) 不允许编译器/优化器从代码中重新排序字段访问。实际上,这就像获取锁一样。

3) 强制编译器/运行时在写入 volatile 变量后立即将其从缓存中刷新到主内存。

4) 在读取 volatile 字段之前将缓存标记为无效。

来自文章:

“Volatile 字段是用于在线程之间进行状态通信的特殊字段。每次读取 volatile 都会看到任何线程对该 volatile 的最后写入;实际上,它们被程序员指定为它所在的字段由于缓存或重新排序而不能看到“陈旧”值。禁止编译器和运行时将它们分配到寄存器中。它们还必须确保在写入后将它们从缓存中刷新到主内存,因此它们可以立即对其他线程可见。类似地,在读取 volatile 字段之前,必须使缓存无效,以便看到主内存中的值,而不是本地处理器缓存。对重新排序还有其他限制访问 volatile 变量。"

... “写入 volatile 字段与监视器释放具有相同的记忆效应,而从 volatile 字段读取具有与监视器获取相同的记忆效应。实际上,因为新的内存模型对 volatile 字段访问的重新排序设置了更严格的限制其他字段访问,可变与否..."

【讨论】:

  • 好答案+1。只需编辑第一个单词,写“According”而不是“ccording”。
  • 哎呀。谢谢,修好了。
猜你喜欢
  • 2020-11-29
  • 2010-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-26
  • 2023-04-08
  • 2020-04-25
相关资源
最近更新 更多