为了账号安全,请及时绑定邮箱和手机立即绑定

Java volatile 重新排序预防范围

Java volatile 重新排序预防范围

慕田峪9158850 2021-06-22 17:14:07
对 volatile 字段的写入和读取分别防止在 volatile 字段之前和之后重新排序读/写。写入 volatile 变量之前的变量读/写不能重新排序在它之后发生,而从 volatile 变量读取之后的读/写不能重新排序在它之前发生。但这项禁令的范围是什么?据我了解, volatile 变量只能防止在使用它的块内重新排序,对吗?为了清楚起见,让我举一个具体的例子。假设我们有这样的代码:int i,j,k;volatile int l;boolean flag = true;void someMethod() {    int i = 1;    if (flag) {        j = 2;    }    if (flag) {        k = 3;        l = 4;    }}显然,write tol将阻止 write tok重新排序,但它会阻止重新排序写入i和j关于l? 换句话说,可以写入i并j在写入之后发生l吗?更新 1感谢大家花时间回答我的问题 - 我很感激。问题是你回答了错误的问题。我的问题是关于范围,而不是关于基本概念。问题基本上是编译器在代码中保证“发生在之前”与 volatile 字段的关系有多远。显然编译器可以保证在同一个代码块内,但是封闭块和对等块呢 - 这就是我的问题。@Stephen C 说,volatile 保证发生在整个方法主体内部的行为之前,即使在封闭块中,但我找不到任何确认。他说得对吗,有什么地方可以确认吗?让我再举一个关于范围界定的具体例子来澄清事情:setVolatile() {   l = 5;} callTheSet() {   i = 6;   setVolatile();}i在这种情况下,编译器会禁止重新排序写入吗?或者编译器不能/没有被编程来跟踪在 volatile 情况下其他方法中发生的事情,并且i可以重新排序写入之前发生setVolatile()?或者编译器根本不重新排序方法调用?我的意思是在某处必须有一个点,当编译器将无法跟踪某些代码是否应该在某些 volatile 字段写入之前发生时。否则,一个易失性字段写入/读取可能会影响一半程序的排序,如果不是更多的话。这是一种罕见的情况,但它是可能的。此外,看看这个报价在新的内存模型下,volatile 变量不能相互重新排序仍然是正确的。不同之处在于现在不再那么容易对它们周围的正常字段访问重新排序。“在他们旁边”。这句话暗示,有一个范围,其中 volatile 字段可以防止重新排序。
查看完整描述

3 回答

?
互换的青春

TA贡献1797条经验 获得超6个赞

显然,写入 l 会阻止写入 k 的重新排序,但它会阻止写入 i 和 j 的重新排序吗?

重新排序的含义并不完全清楚;看我上面的评论。

然而,在Java 5+内存模型,我们可以说,写入ij所发生之前,写入l将是另一个线程可见后已经阅读l......只要没有写ij写后l

这确实会限制写入i和的指令的任何重新排序j。具体来说,它们不能在写入后的内存写入屏障之后移动到l,因为这可能导致它们对第二个线程不可见。

但这项禁令的范围是什么?

本身没有禁令。

您需要了解指令、重新排序和内存屏障只是实现 Java 内存模型的特定方式的细节。该模型实际上是根据保证在任何“格式良好的执行”中可见的内容来定义的。

据我了解, volatile 会阻止在使用它的块内重新排序,对吗?

实际上,没有。块不考虑。重要的是方法中语句的(程序源代码)顺序。


@Stephen C 说,volatile 保证发生在整个方法主体内部的行为之前,即使在封闭块中,但我找不到任何确认。

确认是 JLS 17.4.3。它声明如下:

在每个线程 t 执行的所有线程间动作中,t 的程序顺序是一个总顺序,它反映了根据 t 的线程内语义执行这些动作的顺序。

如果所有动作以与程序顺序一致的总顺序(执行顺序)发生,则一组动作是顺序一致的,此外,变量 v 的每次读取 r 都会看到写入 w 写入 v 的值,使得:

  • w 在执行顺序中排在 r 之前,并且

  • 在执行顺序中,没有其他写入 w' 使得 w 在 w' 之前并且 w' 在 r 之前。

顺序一致性是对程序执行中的可见性和顺序的非常有力的保证。在顺序一致的执行中,所有单独的动作(例如读取和写入)都有一个与程序顺序一致的总顺序,每个单独的动作都是原子的,对每个线程都是立即可见的。

如果一个程序没有数据竞争,那么程序的所有执行都将看起来是顺序一致的。

请注意,此定义中没有提及块或范围。


查看完整回答
反对 回复 2021-06-23
?
catspeake

TA贡献1111条经验 获得超0个赞

我很想知道 volatile 变量如何影响其他字段

易失性变量确实会影响其他字段。如果 JIT 编译器认为重新排序不会对执行输出产生任何影响,则可以对指令重新排序。因此,如果您有 6 个独立变量存储,则 JIT 可以重新排序指令。

但是,如果您将变量设为 volatile,即在您的情况下为变量l,那么 JIT 将不会在 volatile STORE 之后重新排序任何变量 STORES。我认为这是有道理的,因为在多线程程序中,如果我将变量l的值设为 4,那么我应该将i设为 1,因为在我的程序中,是在l之前编写的,最终是程序顺序语义(如果我是没有错)。

Note that volatile variables does two things:
  1. 编译器不会在易失性存储之后重新排序任何存储/在易失性读取之前不会重新排序任何读取。

  2. 刷新加载/存储缓冲区,以便所有处理器都可以看到更改。

编辑:

这里的好博客:http : //jpbempel.blogspot.com/2013/05/volatile-and-memory-barriers.html


查看完整回答
反对 回复 2021-06-23
  • 3 回答
  • 0 关注
  • 161 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信