Java如何管理volatile字段的可见性?

时间:2014-10-05 16:50:01

标签: java multithreading volatile happens-before

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

Java中的volatile关键字用于在对该变量的读取操作完成后立即使该变量“主动”可见。这是发生之前关系的一种形式 - 使写入的结果暴露给访问该变量的内存位置以供某些使用的人。并且在使用时,对该变量进行读/写操作原子 - 对于long& double以及 - 其他所有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(),则将close的值设置为true是一个原子操作,即使它未被声明为volatile

此代码中还有volatile正在做的事情是确保在发生这种情况时可以看到对此值的更改。

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

通过优先考虑对volatile变量进行操作的线程?如果是这样 - 如何,在线程调度中,或通过使线程与读取操作一起查找一个标志,看看是否有一个编写器线程挂起?我知道“在每次后续读取同一个字段之前,会发生对易失性字段的写入。”它是在线程中选择的,在为只读取的线程提供CPU时间之前,对volatile变量进行写操作的线程是什么?

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

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

TIA。

2 个答案:

答案 0 :(得分:3)

这是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.  

我希望它有所帮助。

答案 1 :(得分:3)

根据这个:

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

Java的新内存模型通过

实现

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

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

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

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

来自文章:

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

... “写入易失性字段与监视器释放具有相同的记忆效应,从易失性字段读取具有与监视器获取相同的记忆效应。实际上,因为新的存储器模型对重新排序易失性字段访问设置了更严格的限制其他字段访问,不稳定或不...“