Java内存模型 - 令人惊讶的行为

3
我正在阅读 JSR-133 中有关 Java 内存模型的内容,但我无法理解这种行为如何被接受:
enter image description here 请问有人可以解释一下吗?

那个,以及x在线程之间共享且操作不是原子的这个事实。 - jwenting
你们两个的答案都是不正确的。 - paladin
1
是的,我注意到了。但我不理解问题,8.1明确描述了所需的、稍微晦涩但可以想象的架构。 - tevemadar
严格按照“JLS”的规定,这被称为数据竞争。对于数据竞争来说,任何事情都有可能发生,如果您能够指出您在哪里看到了这个例子,那将非常有帮助。 - Eugene
2个回答

1
8.1 内存模型允许的令人惊讶的行为 图12展示了一个小而有趣的例子。 r1 == 2 和 r2 == 1 的行为是合法的,尽管可能很难看出它如何发生。编译器不会重新排序每个线程中的语句; 这段代码不能导致 r1 == 1 或 r2 == 2。但是,在处理器架构中执行写入操作时,r1 == 2 和 r2 == 1 的行为可能是允许的,但是以一种方式进行早期写入,以致在程序顺序中排在它们之前的本地读取不可见。尽管这种行为令人惊讶,但Java内存模型允许此行为。要在内存模型中获得此结果,我们提交两个写入,然后是两个读取。

不知何故,CPU决定先写入x,然后再读取x。这个例子所说的就是这是有效的行为,或多或少地,这是被接受为有效行为的异常情况。

英特尔Itanium CPU可以产生这样的行为。

因此,与其:

//Thread 1
int x = 0;
int r1 = x;
x = 1;

//Thread 2
int x = 0;
int r2 = x;
x = 2;

这是发生的事情:

//Thread 1
int x = 0;
x = 2; //from Thread 2
int r1 = x;

//Thread 2
int x = 0;
x = 1; //from Thread 1
int r2 = x;

这是完全有效的。(被接受的例外。)


问题中的合法行为是 r1 == 2r2 == 1。您的示例显示 r1 == 1r2 == 2,而“编译器不会重新排序每个线程中的语句; [...]”。 - akuzminykh
谢谢,我已经纠正了。 - paladin
@paladin,CPU如何将[r1 = x,x = 5]重新排列为[x = 5,r1 = x]?这会破坏所有程序吗?谢谢。 - Hlib
这与“权限”无关,而是与硬件限制有关。某些CPU只能以特定的方式处理某些代码,这就是为什么存在这个异常。我同意您的观点,如果不加处理,这个异常会导致软件错误,这就是为什么它被API记录并定义为有效的原因。因此,程序员必须注意这种无聊的问题;-) 我非常确定通常的主流CPU不会受到这种行为的影响。 - paladin

1
CPU唯一需要确保的是线程内对X的写入不会影响其关联RX内存位置的后续赋值。它并没有说明从何处获取要写入的值。
因此,在线程1中,CPU会说
“哦,我需要读取X”,然后开始读取操作。
它接着说
“我还需要写入X”,于是把值排入了一个写队列。
线程2也做同样的事情。
“哦,我需要读取X”,并开始读取。
“我需要写入X”,然后将写入排入队列。
现在我们有了两个等待读取和两个排队写入。
如果CPU架构规定一个核心上的读取可以查询另一个核心的写入队列,则两个核心都可以读取彼此未完成的写入到X的值。因此你会得到这两个值被跨核心拉取,最终被分配给该线程的RX内存位置。
当在指令流中放置内存屏障时,可以防止这种过度渴望的排队写入读取发生。

r1r2不是“内存位置”,或者至少没有其他线程引用它们。在CPU术语中,它们是寄存器,这就是为什么它们有像r1这样的名称。半相关:https://preshing.com/20120515/memory-reordering-caught-in-the-act/,但那是关于StoreLoad重新排序,而不是必要的LoadStore重新排序,以创建所询问的条件。(除了这是对同一内存位置上的操作重新排序,这是不寻常的。) - Peter Cordes
@PeterCordes Java虚拟机是一种栈机,因此没有任何(通用)寄存器。所有操作码都与堆栈上的值相关定义,因此我假设r1和r2是代码中的变量。同样,寄存器是内存,它们只是位于芯片上。 - Ben Seidel
在JVM术语中,这个litmus测试中的r1r2将是Java堆栈上的数据。是的,要查看从共享内存加载的内容,您需要稍后将这些加载结果复制到可以收集它们的地方(例如内存中的本地变量),就像汇编版本的litmus测试中的CPU寄存器一样。 (CPU寄存器对于每个核心都是私有的。它们仅在存储数据并且物理上由SRAM制成(除了寄存器重命名之外),它们不具有通常意义上的地址,并且不能间接寻址。) - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接