5

我一直在阅读有关 Java 内存模型的内容,并且我知道编译器可以重新组织语句以优化代码。

假设我有以下代码:

long tick = System.nanoTime();
function_or_block_whose_time_i_intend_to_measure();
long tock = System.nanoTime();

编译器是否会以我打算测量的内容不在滴答和滴答之间执行的方式重新组织代码?例如,

long tick = System.nanoTime();
long tock = System.nanoTime();
function_or_block_whose_time_i_intend_to_measure();

如果是这样,保持执行顺序的正确方法是什么?

编辑:用 nanoTime 说明乱序执行的示例:

public class Foo {
    public static void main(String[] args) {
        while (true) {
            long x = 0;

            long tick = System.nanoTime();
            for (int i = 0; i < 10000; i++) { // This for block takes ~15sec on my machine
                for (int j = 0; j < 600000; j++) {
                    x = x + x * x;
                }
            }

            long tock = System.nanoTime();
            System.out.println("time=" + (tock - tick));
            x = 0;
        }
    }
}

上述代码的输出:

time=3185600
time=16176066510
time=16072426522
time=16297989268
time=16063363358
time=16101897865
time=16133391254
time=16170513289
time=16249963612
time=16263027561
time=16239506975

在上面的示例中,第一次迭代中测量的时间明显低于后续运行中测量的时间。我认为这是由于乱序执行造成的。第一次迭代我做错了什么?

4

2 回答 2

0

happens-beforeJava 内存模型 (JMM) 定义了对程序的所有操作调用的偏序。定义了七个规则来确保happens-before排序。其中之一被称为Program order rule

节目顺序规则。线程中的每个动作都发生在该线程中按程序顺序稍后出现的每个动作之前。

根据这个规则,你的代码不会被编译器重新排序。

Java Concurrency in Practice一书对这个主题给出了很好的解释。

于 2013-03-25T08:31:56.247 回答
0

编译器是否会以我打算测量的内容不在滴答和滴答之间执行的方式重新组织代码?

没有。那永远不会发生。如果那个编译器优化搞砸了,那将是一个非常严重的错误。引用维基的声明。

运行时(在这种情况下,通常指动态编译器、处理器和内存子系统)可以自由地引入任何有用的执行优化,只要保证隔离线程的结果与其完全相同如果所有语句都按照语句在程序中出现的顺序(也称为程序顺序)执行,则应该是这样。

因此,只要结果与按程序顺序执行时的结果相同,就可以进行优化。在您引用的情况下,我会假设优化是本地的,并且没有其他线程对此数据感兴趣。进行这些优化是为了减少对主内存的访问次数,这可能是昂贵的。当涉及多个线程并且它们需要知道彼此的状态时,您只会遇到这些优化的麻烦。

现在,如果 2 个线程需要一致地查看彼此的状态,它们可以使用 volatile 变量或内存屏障(同步)来强制将写入/读取序列化到主内存。Infoq 对此发表了一篇不错的文章,您可能会感兴趣。

于 2013-03-25T06:35:16.863 回答