2017-06-04 35 views
0

我试图调查在java环境中重新排序(使用JDK 9-ea + 170)的行为,并发现我无法为自己解释的一件事,所以我很乐意听到关于它的一些说明。下面是一个例子:为什么使用两个volatile变量重新排序?

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++;指令()的顺序。这样我证明重新排序确实发生。程序大部分时间结束(这是预期的)。 现在我已经加入volatile修饰符到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); 
    } 
} 

这个程序永远不会结束,因为挥发性保证了所有的前挥发的指令会被冲入所以x++;总是y++;之前执行的内存,这是不可能有y> x。这也是我的理解所期待的。但在那之后我已经添加挥发性到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); 
    } 
} 

为什么这里的重新排序也发生?

回答

3

这不是重新排序的证据。实际上,volatile的结果是++不是原子的。例如,更新的一个变量(x)时考虑两个线程(AB)以下的操作交织:

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偶尔会失败就足够了。

(以下简称“数不清”的行为也可能与y发生......这解释了为什么这个实验只失败了一些时间。)

+0

能否请您提供一个例子怎么可能是B> A? 在上面描述的流程中,赋值给A发生在赋值给B之前。 –

+0

1)变量是'x'和'y'。 'A'和'B'表示线程。 2)所有需要发生的事情是,你在'x'上的损失比在'y'上损失更多,然后'y <= x'将会是'false'。 –

+0

感谢您的更新!现在对我来说有点干净了。斯蒂芬,现在我运行相同的代码,但在test()上使用同步关键字。它在volatile或y和x和y上工作正常,但在没有volatile的情况下(这是预期的)失败并且仅在x上volatile时失败。这是正常的行为吗?请解释你是否知道发生了什么 –