- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我对下面的代码段有疑问。结果可能有 [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
现在可以保证,如果您看到 guard
为 1
,您也会观察到 x
为1
(此时x = 1
不能在guard = 1
下面重新排序)。因此,1, 0
现在是非法的,因此不会出现在输出中。
关于Java用非 volatile 重新排序 volatile 写入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47112036/
这将是一篇很长的文章,为了将其上下文化并提供尽可能多的信息,我必须仔细浏览各种链接和引号——这通常是我们进入 C/C++ 标准兔子洞的唯一方法。如果您对这篇文章有更好的引用或任何其他改进,请告诉我。但
我想知道 volatile 关键字与 register、const 和 static 结合的不同用途关键词。我不确定有什么影响,所以我认为: register volatile int T=10; 建
让我们考虑以下 Java 代码 int x = 0; int who = 1 Thread #1: (1) x++; (2) who = 2; Thread #2 while(who
有一个函数“remove_cv”(http://en.cppreference.com/w/cpp/types/remove_cv)可以删除常量和 volatile 。 我的问题是为什么可以从“con
我正在尝试在下面的“MpscQueue.h”中的嵌入式目标上实现多个生产者(通过中断)、单个消费者(通过应用程序线程)队列。 我想知道我是否可以安全地删除一些 volatile下面的用法(见内联问
我的问题适用于最初为 null 的字段,然后初始化为非 null 值,然后不再更改。 由于该字段需要尽快可供所有线程使用,因此我需要使用 volatile 。 但是,如果我想尽可能避免 volatil
我以前见过几次类似 fld = fld 的东西,但在所有这些情况下,可以消除虚拟写入并获得更好的性能。 public class Tst{ public volatile int fld =
看完this question和 this (尤其是第二个答案)我对 volatile 及其与内存屏障有关的语义感到非常困惑。 在上面的例子中,我们写入了一个 volatile 变量,这会导致一个 m
如下所示,该程序有一个共享 var flag,但不带 volatile : public class T { public static void main(String[] args) {
我明白声明 int *volatile ptr; 表示指针本身是volatile int a=10; int *volatile ptr=&a; 现在 ptr 和 a 都在更新。会不会导致访问ptr时
最近我需要比较两个 uint 数组(一个是 volatile 数组,另一个是非 volatile 数组),结果令人困惑,我一定是对 volatile 数组有一些误解。 我需要从输入设备读取一个数组并将
这两个 C 定义有什么区别? volatile uint32_t *ptr1 = (volatile uint32_t *)0x20040000; volatile uint32_t *ptr1 =
// structure is like this, but not exact formation. class queue { volatile List worksWaiting; }
考虑以下这段代码: struct S{ int i; S(int); S(const volatile S&); }; struct S_bad{ int i; }; vola
在 Windows x64 上,考虑到一些额外的见解,何时允许编译器将 ABI 标记为 volatile 的寄存器视为非 volatile 寄存器?我有一个反汇编函数,其中 r11 用于在函数调用后恢
我对下面的代码段有疑问。结果可能有 [0, 1, 0] 的结果(这是用 JCStress 执行的测试)。那么这怎么会发生呢?我认为应该在写入 Actor2 (guard2 = 1) 中的 guard2
好吧,假设我有一堆变量,其中一个声明为 volatile: int a; int b; int c; volatile int v; 如果一个线程写入所有四个变量(最后写入 v),而另一个线程读取所有
我试图理解为什么这个例子是一个正确同步的程序: a - volatile Thread1: x=a Thread2: a=5 因为存在冲突访问(存在对 a 的写入和读取),所以在每个顺序一致性执行中,
我正在编写一个需要同时支持 volatile 和非 volatile 实例的类( volatile 实例使用原子操作,非 volatile 实例使用常规操作),并且想知道我是否以正确的方式进行处理。到
我正在为 Cortex-M0 CPU 和 gcc 编写代码。我有以下结构: struct { volatile unsigned flag1: 1; unsigned flag2: 1
我是一名优秀的程序员,十分优秀!