gpt4 book ai didi

Java用非 volatile 重新排序 volatile 写入

转载 作者:搜寻专家 更新时间:2023-11-01 03:32:14 29 4
gpt4 key购买 nike

我对下面的代码段有疑问。结果可能有 [0, 1, 0] 的结果(这是用 JCStress 执行的测试)。那么这怎么会发生呢?我认为应该在写入 Actor2 (guard2 = 1) 中的 guard2 之前执行数据写入 (data = 1)。我对吗?我问,因为我读过很多次关于 volatiles 的指令没有重新排序。此外根据这个:http://tutorials.jenkov.com/java-concurrency/volatile.html它是这样写的:

The reading and writing instructions of volatile variables cannot be reordered by the JVM (the JVM may reorder instructions for performance reasons as long as the JVM detects no change in program behaviour from the reordering). Instructions before and after can be reordered, but the volatile read or write cannot be mixed with these instructions. Whatever instructions follow a read or write of a volatile variable are guaranteed to happen after the read or write.

所以如果我们不能重新排序

  public class DoubleVolatileTest {

volatile int guard1 = 0;
int data = 0;
volatile int guard2 = 0;

@Actor
public void actor1() {
guard2 = 1;
data = 1;
guard1 = 1;
}

@Actor
public void actor2(III_Result r) {
r.r1 = guard1;
r.r2 = data;
r.r3 = guard2;
}

}

提前致谢!

最佳答案

首先,这个:

The reading and writing instructions of volatile variables cannot be reordered by the JVM...

表示 volatile themselves 不能重新排序(volatile 与 volatile 就是这样);但需要注意的是

the JVM may reorder instructions for performance reasons as long as the JVM detects no change in program behaviour from the reordering.

一般来说,关于 JVM 的重新排序(可能完成或不完成)的推理是不正确的(我读过关于 volatiles 的指令没有重新排序 ...)。重新排序/障碍/等不是 JLS 的一部分;相反,它在 happens-before 规则的前提下运行,这是您唯一应该关心的事情。

您的示例确实可以按照评论中的说明进行简化:

@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "the one we care about")
@Outcome(id = "1, 1", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@Outcome(id = "0, 1", expect = Expect.ACCEPTABLE, desc = "don't care about this one")
@JCStressTest
@State
public class VolatileTest {


private volatile int guard = 0;
private int x = 0;


@Actor
void writeActor() {
guard = 1; // volatile store

// your reasoning is that these two operations should be re-ordered
// unfortunately, this is not correct.

x = 1; // plain store
}

@Actor
void readActor(II_Result r) {

r.r1 = x; // plain store
r.r2 = guard; // plain store

}
}

运行这将导致 1, 0 确实,这意味着 x = 1 确实被 guard = 1 重新排序;实际上,实际上还可能发生更多其他事情(但为简单起见,我们称它们为重新排序,尽管这不是您可以观察到 [1, 0] 的唯一原因)。

在 JLS 术语中:您尚未在这些操作之间建立任何之前发生的(例如典型的 volatile 存储/ volatile 加载)- 因此这些操作可以自由 float 大约。这几乎就是答案的结尾。更广泛的解释是 volatile (因为你使用过它),是这样说的:

A write to a volatile field happens-before every subsequent read of that same field.

您没有对volatile guard 进行任何 读取,因此无法保证任何事情。另一种解释方式是 this excellent article甚至 this one .但即使您确实阅读了 guard,仍然无法保证重新排序,因为您的代码设置方式。


volatile 仅在成对使用时才能正常工作,即 Thread1 写入 volatile 字段 - Thread2 观察写入。在这种情况下,按程序顺序写入之前完成的所有操作都将被Thread2 看到(显然是在看到该写入值之后)。或者在代码中:

 public class VolatileTest {

private volatile int guard = 0;
private int x = 0;


@Actor
void writeActor() {

// store comes "before" the store to volatile
// as opposed to the previous example
x = 1; // plain store
guard = 1; // volatile store
}

@Actor
void readActor(II_Result r) {

r.r1 = guard; // plain store
r.r2 = x; // plain store

}
}

JLS 现在可以保证,如果您看到 guard1,您也会观察到 x1(此时x = 1 不能在guard = 1 下面重新排序)。因此,1, 0 现在是非法的,因此不会出现在输出中。

关于Java用非 volatile 重新排序 volatile 写入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47112036/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com