【发布时间】:2020-01-04 23:01:40
【问题描述】:
对 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;
}
}
显然,写入l 将阻止写入k 重新排序,但它会阻止写入i 和j 相对于l 重新排序?换句话说,写入i 和j 可以在写入l 之后发生吗?
更新 1
感谢大家抽出宝贵时间回答我的问题 - 我很感激。问题是你回答错了问题。我的问题是关于范围,而不是关于基本概念。问题基本上是在代码中,编译器能保证与 volatile 字段的“发生在之前”的关系。 显然编译器可以保证在同一个代码块内,但是封闭块和对等块呢 - 这就是我的问题所在。 @Stephen C 说,不稳定的保证发生在整个方法体内的行为之前,即使在封闭块中,但我找不到任何确认。他说的对吗,有什么地方可以确认吗?
让我再举一个关于范围界定的具体例子来澄清事情:
setVolatile() {
l = 5;
}
callTheSet() {
i = 6;
setVolatile();
}
在这种情况下,编译器会禁止i write 的重新排序吗?或者也许编译器不能/没有被编程来跟踪在 volatile 的情况下在其他方法中发生的情况,并且i write 可以重新排序以在setVolatile() 之前发生?或者编译器根本不会重新排序方法调用?
我的意思是在某个地方必须有一个点,当编译器将无法跟踪某些代码是否应该在某些易失性字段写入之前发生。否则,一个易失性字段写入/读取可能会影响一半程序的排序,如果不是更多的话。这是一种罕见的情况,但有可能。
另外,看看这句话
在新的内存模型下,volatile 变量之间不能相互重新排序仍然是事实。不同之处在于,现在对围绕它们的正常字段访问重新排序不再那么容易了。
“围绕着他们”。这句话暗示,有一个 volatile 字段可以防止重新排序的范围。
【问题讨论】:
-
关于什么重新排序?彼此?写信给
l?还有什么? -
当然是关于“l”。我认为这很清楚,但我会说得更清楚。谢谢。
-
@NikKotovski 你是在问
i或j是否可以在写到l下方重新排序? -
我喜欢认为这是一个障碍和可以向下/向上跳跃(重新排序)的操作(按顺序或阅读代码)。这可能有助于stackoverflow.com/questions/45151763/…。但要回答您的问题,这些是独立阅读,高于
l(障碍)任何东西都可以重新排序。关键是其他一些线程似乎是l的写(观察它),他们也会观察写它i和j- 因此你并不真正关心这个内部重新排序(如果它发生了) -
@JohnVint 是的。由于它们在其他块中,
i甚至在封闭块中,它们可以重新排序以在l之后发生吗?
标签: java multithreading concurrency volatile