【问题标题】:Volatile and more threads易变多线程
【发布时间】:2014-07-16 20:31:13
【问题描述】:

我正在尝试了解 volatile 关键字及其正确使用方法。看看 Brian Goetz 的文章Java theory and practice: Fixing the Java Memory Model,我被这个例子困住了:

Map configOptions;
char[] configText;
volatile boolean initialized = false;

// In Thread A
configOptions = new HashMap();
configText = readConfigFile(fileName);
processConfigOptions(configText, configOptions);
initialized = true;

// In Thread B
while (!initialized) 
    sleep();
// use configOptions

上面的 volatile 变量用作“守卫”,表明一组共享变量已被初始化。

我知道,从 java 1.5 开始,volatile 足够强大,可以确保当线程 B 读取 volatile 变量时,它会看到线程 A 在线程 A 时可见的所有变量写入 volatile 变量。

但是如果有一个线程 C 做这样的事情呢:

// In Thread C
configOptions = new HashMap();
// put something to configOptions

我的问题:volatile 是否足够强大,以确保当线程 B 读取 volatile 变量时,它会看到来自所有线程的 all 变量。也许某种刷新所有缓存?如果不是,那么这种3线程的代码就坏了,对吧?

【问题讨论】:

  • 您正在阅读 2004 年的一篇文章。此后内存模型发生了变化。
  • 最好使用基于 Atmoic 的对象,例如 AtomicBoolean
  • 我认为它在 2004 年发生了变化。
  • @Kayaman 自 Java 1.5 以来我还没有看到它发生变化。自 JSR 133 以来它发生了怎样的变化?我想我的更好的问题是,它是如何在 2004 年编写的使所提出的观点无效?

标签: java concurrency volatile


【解决方案1】:

volatile 是否足够强大,以确保当线程 B 读取 volatile 变量,它查看来自所有线程的所有变量。也许有些 刷新所有缓存?

在 volatile 存储之前写入的 volatile-writing-thread 中的所有变量都是可见的。

所以没有“刷新所有缓存”的魔法。

如果不是,那么这样的3线程代码就坏了,对吧?

如果您没有正确同步,它很可能会被两个线程破坏。初始化标志被写入是有原因的。这有效地刷新了该线程上发生的所有写入。

【讨论】:

    【解决方案2】:

    在这个答案中,我将尝试解释 Java 中的 volatile 变量是什么。

    那么,从哪里开始呢?

    1. volatile 变量的读写操作保证是原子的,即使对于 64 位长度的变量也是如此。注意:i++; 不是原子的,因为从技术上讲它是三个变量。

    2. 将一些值写入volatile 变量发生之前可以从中读取该值。您可以在 SO 上找到很多关于 happen-before 是什么的问题。 重要:在 JVM 中,它使用内存栅栏实现,store 栅栏用于写入,load 栅栏用于读取。从实际角度来看,这意味着当您从中读取一些值时,可以保证在 volatile 写入之前看到所有写入非易失性变量的值;

    3. 写入 volatile 变量的值一次可供所有 CPU 和所有线程使用,无需任何 CPU 缓存。

    现在,关于你的问题。

    volatile 是否足够强大,以确保线程 B 在读取 volatile 变量时,可以看到来自所有线程的所有变量?

    没有。它足够强大,可以确保当线程 B 从volatile 变量中读取某个值时,它会看到(将读取)来自易失性写入之前写入的变量中的值。

    也许某种刷新所有缓存?

    实际上是的,在 x86 架构上,volatile write 清空存储顺序缓冲区,volatile read 清空加载顺序缓冲区。如果您想了解更多详细信息,您可能需要阅读此问题的答案:Java 8 Unsafe: xxxFence() instructions

    如果不是,那么这样的3线程代码就坏了,对吧?

    此代码按预期工作(我猜),因为线程 B 在读取 configOptions 之前会进行 volatile 读取,这保证了它的可见性。

    【讨论】:

      【解决方案3】:

      根据语言规范 (http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.4):

      对 volatile 变量 v(第 8.3.1.4 节)的写入与任何线程对 v 的所有后续读取同步(其中“后续”根据同步顺序定义)。

      对 volatile 字段(第 8.3.1.4 节)的写入发生在对该字段的每次后续读取之前。

      因此 volatile 变量本身不会出现过时缓存问题。你的问题是; “所有其他变量呢?”不,volatile 关键字只影响它所在变量的缓存:这些线程上的所有其他变量都是不同步的。

      【讨论】:

      • “volatile 关键字只影响它所在变量的缓存”这不是真的。阅读我在问题中链接的文章。尤其是“在什么之前会发生什么?”的部分
      猜你喜欢
      • 1970-01-01
      • 2013-07-06
      • 1970-01-01
      • 2011-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      相关资源
      最近更新 更多