因为 volatile 写入后的普通读写不禁止重排序,所以下面代码中的 b=3 可能会在 a=2+b 之前重排序吗?
volatile int a = 1;
int b = 2;
private void demo () {
a = 2 + b;
b = 3;
}
因为 volatile 写入后的普通读写不禁止重排序,所以下面代码中的 b=3 可能会在 a=2+b 之前重排序吗?
volatile int a = 1;
int b = 2;
private void demo () {
a = 2 + b;
b = 3;
}
不完全是。
需要注意的重要一点是,给定两个事件,两个线程可以以不同的顺序观察这两个事件。
由于这两个写入都在一个线程中,因此该线程保证按照它们出现的顺序观察这两个写入。因此,该语句没有风险a = 2 + b
将使用即将设置的值b
(即 3)而不是其已经设置的值(大概是 2,尽管它显然可以依赖于您未显示的其他代码)。
这意味着a
最终将根据需要设置为 4,而不是5。
但是其他线程当然可以观察到这些乱序的写入;如果另一个线程有System.out.println("a = " + this.a + ", b = " + this.b)
,并且这里没有其他同步,那么该线程绝对有可能打印a = 1, b = 3
,显示 的原始值,a
但显示 的新值b
。
不能违反程序的顺序语义。根据您的问题,我的印象是演示方法是由单个线程执行的,因此 a 的易失性不相关。
如果我将您的代码简化为单独的加载/存储:
r1=b
a=2+r1
b=3
唯一可能的允许结果是 a=4,b=3。
然后 b=3 和 a=2+r1 可以重新排序,因为它不会改变结果。但是如果 b=3 会跳到 r1=b 前面,那么这个执行的结果就是 a=5, b=3。这违反了程序的顺序语义。
有关更多信息,请参阅我在这篇文章中的回答: 为什么 Java Concurrency in Practice 中的这个示例不允许重新排序