2012-11-18 26 views
4

让我们下面的语句:独立指令可以通过Java编译器或JVM交换指令顺序吗?

int d0, d1; 
int[] ds = {0, 0}; 

现在一个线程具有以下说明:

d0++; 
d1++; 

而另一个线程指令:

ds[1] = d1; 
ds[0] = d0; 

如果我们并行运行这些线程,显然有三种组合可以看作:{0,0},{1,1}和{1,0}。

现在最大的问题是:还可以有{0,1}吗?编译器/ JVM可以简单地交换指令,因为它认为它们不相关?如果是,那么这些行为的“规则”究竟是什么,是由编译器还是JVM决定?

+1

通常优化被认为是安全的,如果它们的行为与串行(iow,一个线程)模型中未优化的代码相同。除非相关的语言结构对线程有明确的规定 –

回答

6

是的,{0, 1}也是可能的。 Java内存模型不足以保证在这种情况下的排序。这甚至不需要指令重新排序 - 如果您在x86或x86_64以外的任何地方运行该程序,无论如何都会发生这种情况。

在这里要清楚,实际的CPU硬件将重新排序这些加载和存储,而不是如果它是x86。

Java Memory Model FAQ

0

是两者的编译器和JVM(刚刚实时编译器)可以做的指令重新排序。而且,硬件处理器可以做到。为了防止不必要的重新排序,应该使用memory barriers

2

如果没有正确的同步,这确实是可能的。

Java语言规范在第17章中定义了多线程Java程序的语义。这一章很难理解,但它确实包含了可以依赖的官方规则。特别是,writes

存储器模型描述给定该程序的程序和执行轨迹,确定执行轨迹是否是程序的合法执行。 Java编程语言内存模型通过检查执行跟踪中的每个读取并根据特定规则检查该读取观察到的写入是否有效来工作。

存储器模型描述程序的可能行为。一个实现可以自由产生任何它喜欢的代码,只要一个程序的所有执行结果产生一个可以被内存模型预测的结果。

为了给出一个粗略的概貌,内存模型定义了happens-before relation任何重新排序必须consistent with。通常的建立方式在不同线程执行动作之前发生,例如synchronize这些动作,例如使用​​块或写入或读取易失性变量。

在没有这种同步的情况下,运行时将独立执行线程,允许任何重新排序的线程无法观察到的当前线程。

也就是说,如果你有可变的共享状态,你通常需要同步访问它的线程。