Java编译器或JVM可以交换指令顺序的独立指令吗?

时间:2012-11-18 13:00:48

标签: java compiler-construction jvm instructions

让我们采取以下声明:

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

现在一个帖子有以下说明:

d0++;
d1++;

而另一个线程有这个指令:

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

如果我们并行运行这些线程,那么ds显然有三种组合:{0,0},{1,1}和{1,0}。

现在最大的问题是:还能有{0,1}吗?编译器/ JVM可以简单地交换指令,因为它认为它们是无关的吗?如果是,那么"规则究竟是什么"对于这种行为,是由编译器还是JVM组成?

3 个答案:

答案 0 :(得分:6)

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

要明确这一点,实际的CPU硬件将重新排序这些加载和存储,而不是它是否为x86。

请参阅Java Memory Model FAQ

答案 1 :(得分:2)

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

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

  

在给定程序和该程序的执行跟踪的情况下,存储器模型描述执行跟踪是否是程序的合法执行。 Java编程语言内存模型的工作原理是检查执行跟踪中的每个读取,并根据某些规则检查该读取所观察到的写入是否有效。

     

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

为了粗略概述,内存模型定义happens-before relation任何重新排序必须为consistent with。建立在不同线程执行的操作之前发生的常用方法是synchronize这些操作,例如使用synchronized块或写入或读取volatile变量。

如果没有这种同步,运行时将独立执行线程,允许重新排序当前线程无法观察到。

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

答案 2 :(得分:0)

是编译器和jvm(即时编译器)都可以进行指令重新排序。而且,硬件处理器可以做到。为防止不必要的重新排序,应使用memory barriers