23

在《Effective Java》中,作者提到了

while (!done) i++;

可以通过 HotSpot 优化成

if (!done) {
    while (true) i++;
}


我对此感到非常困惑。该变量done通常不是const,为什么编译器可以这样优化?

4

4 回答 4

29

作者假设该变量done是一个局部变量,在 Java 内存模型中没有任何要求将其值暴露给其他线程而无需同步原语。或者换一种说法:done除此处显示的内容外,任何代码都不会更改或查看 的值。

在这种情况下,由于循环不会改变 的值done,它的值可以被有效地忽略,并且编译器可以在循环外提升对该变量的评估,防止它在循环的“热”部分被评估. 这使得循环运行得更快,因为它需要做的工作更少。

这也适用于更复杂的表达式,例如数组的长度:

int[] array = new int[10000];
for (int i = 0; i < array.length; ++i) {
    array[i] = Random.nextInt();
}

在这种情况下,简单的实现将评估数组的长度 10,000 次,但由于变量数组从未被分配并且数组的长度永远不会改变,因此评估可以更改为:

int[] array = new int[10000];
for (int i = 0, $l = array.length; i < $l; ++i) {
    array[i] = Random.nextInt();
}

与提升无关的其他优化也适用于此。

希望有帮助。

于 2012-02-18T03:38:55.647 回答
2

Joshua Bloch 的“Effective Java”解释了为什么在线程之间共享变量时必须小心。如果在线程之间的关系之前不存在任何显式发生,则允许 HotSpot 编译器出于速度原因优化代码,如dmide所示。

现在大多数微处理器都提供不同种类的乱序策略。这导致了弱一致性模型,这也是 Java 平台内存模型的基础。背后的想法是,只要程序员没有明确表达对线程间协调的需求,处理器和编译器就可以进行不同的优化。

两个关键字volatile(原子性&可见性)和synchronized(原子性&可见性&互斥)用于表达更改的可见性(对于其他线程)。但是,除此之外,您还必须知道发生前发生的规则(参见 Goetz 等人的“Java Concurrency in Practice”第 341f 页 (JCP) 和 Java 语言规范 §17)。

那么,当System.out.println()被调用时会发生什么?见上文。首先,你需要两个System.out.println()电话。一个在 main 方法中(更改后done),一个在启动的线程中(在while循环中)。现在,我们必须考虑 JLS §17 中的程序顺序规则监视器锁定规则这里是简短的版本:您有一个公共锁对象 M。在 A解锁M之前,线程 A 中发生的所有事情在B锁定M的那一刻对另一个线程 B 可见(参见 JCP)。

在我们的例子中,两个线程PrintStreamSystem.out. 当我们查看内部println()时,您会看到调用synchronized(this).

结论:两个线程共享一个共同的锁 M,它被锁定和解锁。System.out.println()“冲洗”变量的状态变化done

于 2021-02-19T07:21:28.523 回答
0

如果你System.out.println("i = " + i);在while循环中添加。提升不起作用,这意味着程序按预期停止。println方法是线程安全的,jvm无法优化代码段?

于 2012-02-20T08:42:28.017 回答
0
public class StopThread {
private static boolean stopRequested;

private static synchronized void requestStop() {
    stopRequested = true;
}

private static synchronized boolean stopRequested() {
    return stopRequested;
}

public static void main(String[] args)
                throws InterruptedException {
    Thread backgroundThread = new Thread(new Runnable() {
        public void run() {
            int i = 0;
            while (!stopRequested())
                i++;
        }
    });
    backgroundThread.start();
    TimeUnit.SECONDS.sleep(1);
    requestStop();
}
}

上面的代码是对的有效代码,相当于volatile用来装饰的stopRequested

private static boolean stopRequested() {
  return stopRequested;
}

如果这个方法省略了synchronized关键字,这个程序就不能正常工作。
我认为当方法省略关键字时,这种变化会导致提升。synchronized

于 2016-10-08T07:26:08.450 回答