【问题标题】:Reorder normal field around volatile field围绕 volatile 字段重新排序正常字段
【发布时间】:2011-02-07 08:49:34
【问题描述】:

基于

volatile 有什么作用? http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#incorrectlySync

volatile 的新保证 http://www.ibm.com/developerworks/library/j-jtp03304/

class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    x = 42;
    v = true;
  }

  public void reader() {
    if (v == true) {
      //uses x - guaranteed to see 42.
    }
  }
}

好像是这样。

1a) write to non-volatile variable x 
1b) write to volatile variable v

1a 永远无法通过 1b

我想知道,如果我将源代码修改为以下内容

class VolatileExample {
  int x = 42;
  volatile boolean v = true;
  public void writer() {
    v = false;
    x = 0;
  }

  public void reader() {
    if (v == true) {
      //uses x - guaranteed to see 42?????
    }
  }
}

下面的序列可以置换吗?

2a) write to volatile variable v
2b) write to non-volatile variable x

我想知道,2b 可以在 2a 之前移动吗?这是因为如果 2b 能够在 2a 之前移动,读者就不能再保证在 if 块中看到 42。

根据以下信息,我觉得 2b 可以在 2a 之前移动。


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

写入 volatile 字段具有 和显示器一样的记忆效应 释放,并从易失性中读取 场具有相同的记忆效应 监控获取。


这意味着任何内存操作 之前对线程可见 退出同步块是 进入后对任何线程可见 受保护的同步块 同一台显示器,因为所有的内存 操作发生在发布之前, 并且释放发生在 获取。


Roach Motels and The Java Memory Model

volatile_v = true;      <-- monitor release
non_volatile_x = 42;
(volatile_v will act as a roach motels, and it is fine for non_volatile_x to move into roach motels)


non_volatile_x = 42;
volatile_v = true;      <-- monitor release
(volatile_v will act as a roach motels, and it is not OK for non_volatile_x to move out from roach motels)

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    我们知道,对 volatile 变量的写入不能相对于任何先前的读取或写入重新排序(从 Java5 开始),但反过来就不是这样了。所以将程序重新排序为 x = 0; v = 假;据我了解是正确的。

    在我们写入 v 之后,我们保证在读取 v 时,在写入 v 之前发生的每个动作都是可见的,但它没有说明写入 v 之后的动作 - 这些可能发生也可能没有发生甚至可以在写入 v 之前重新排序。

    【讨论】:

    【解决方案2】:

    volatile 有两个部分,指令的顺序和写入的“刷新”。

    指令的顺序不会改变,但是两段代码之间可能会有任何延迟,所以在第二种情况下 x 可能是 42 但也可能是 0。我猜你会更频繁地看到 0。

    根据您的架构,这可能比在其他系统上更有可能,因此在一台机器上测试它发现它是 42,并不意味着它总是在另一台机器上。

    【讨论】:

    • 为什么说指令的顺序不会改变?你有什么具体的参考吗?
    • AFAIK,相同的参考文献基本上表明 volatile 上的指令不会向前或向后移动。
    • 操作顺序无关紧要。问题是单独的操作之一。正如@Peter 指出的那样,调用reader() 的线程可能会进入 if 块,然后最终被无限期延迟。当它最终被重新安排执行时,x 现在可能是 0。
    【解决方案3】:

    据我了解新的内存模型,一旦一个 volatile 值被刷新到主内存,所有其他可以被刷新线程看到的变量也必须被刷新。但是这样做的结果是 VM 无法将您的 2b 重新排序到 2a 之前,因为这会违反代码中隐含的“发生之前”排序。

    【讨论】:

    • 将 2b 移到 2a 之前不应违反 'happens-before' 关系。 If one action happens-before another, then the first is visible to and ordered before the second. 在 2a 之前什么都没有发生。因此,将 2b 移到 2a 上方应该没问题。 java.sun.com/docs/books/jls/third_edition/html/…
    【解决方案4】:

    我认为你不能保证这一点,因为类实例字段 x 没有被封装,并且包可见可以由另一个线程更改。将 x 变量设为 volatile 将解决该问题并保证所有线程的可见性。

    【讨论】:

      【解决方案5】:

      可以将语句 2b(写入非易失性变量 x)移到语句 2a(写入易失性变量 v)之前,即有可能另一个线程看到 v==truex==0)。

      请注意,即使不重新排序另一个线程也可能会看到这些值!

      假设VolatileExample 的实例已创建并初始化。线程 T1 在该实例上执行方法 reader。它看到v == true 并被调度程序中断。第二个线程调用方法writer 现在可以执行分配v=falsex=0。当 T1 恢复并读取 x 时,它可能会读取值 0(但不能保证看到 0)。如果它看到值 0,那么我们就会遇到这种情况,即 T1 看到 v==truex==0。在 T1 看来,这两个语句似乎已重新排序。由于无需重新排序即可观察到这种状态,因此实际上允许重新排序是有道理的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-01
        • 2023-03-04
        • 2022-06-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多