我试图调查在java环境中重新排序的行为(使用JDK 9-ea + 170)并发现了一件我无法解释的事情,所以我很高兴听到一些关于它的说明。这是一个例子:
public class Client {
int x;
int y;
public void test() {
x++;
y++;
}
public static void main(String[] args) {
Client c = new Client();
while(c.y <= c.x) new Thread(() -> c.test()).start();
System.out.println(c.x + " " + c.y);
}
}
此程序有test()
方法,只增加x和y值。我正在创建新的线程并调用此test()
,直到某些内部Java优化不会更改为x++; y++;
指令()的顺序。这样我证明重新排序确实发生了。程序在大部分时间结束(这是预期的)。
现在我已经将易挥发性修饰符添加到y:
public class Client {
int x;
volatile int y;
public void test() {
x++;
y++;
}
public static void main(String[] args) {
Client c = new Client();
while(c.y <= c.x) new Thread(() -> c.test()).start();
System.out.println(c.x + " " + c.y);
}
}
此程序永远不会结束,因为volatile会保证volatile之前的所有指令都会被刷新到内存中,因此x++;
总是在y++;
之前执行,并且不可能有y&gt; X。根据我的理解,这也是预期的。但之后我也将{volatile}添加到int x;
,现在我再次看到重新排序,所以程序大部分时间结束:
public class Client {
volatile int x;
volatile int y;
public void test() {
x++;
y++;
}
public static void main(String[] args) {
Client c = new Client();
while(c.y <= c.x) new Thread(() -> c.test()).start();
System.out.println(c.x + " " + c.y);
}
}
为什么还要在这里进行重新排序?
答案 0 :(得分:3)
这不是重新排序的证据。事实上,正在发生的事情是++
volatile
不是原子的结果。例如,在更新其中一个变量(A
)时,请考虑以下两个线程(B
和x
)的操作交错:
thread A: load x -> temp
thread B: load x -> temp
thread A: temp = temp + 1
thread B: temp = temp + 1
thread A: save temp -> x
thread B: save temp -> x
如果您使用该交错处理这些操作,您将看到您已经丢失了x
的计数。这足以让c.y <= c.x
偶尔失败。
(&#34;丢失计数&#34;行为也可能发生在y
...这解释了为什么这个实验只在某些时候失败。)