在《Effective Java》一书中,作者提到:
while (!done) i++;
可以通过HotSpot进行优化,变成
if (!done) {
while (true) i++;
}
我对此非常困惑。变量done
通常不是const,为什么编译器能够进行优化?
while (!done) i++;
可以通过HotSpot进行优化,变成
if (!done) {
while (true) i++;
}
我对此非常困惑。变量done
通常不是const,为什么编译器能够进行优化?
done
是一个局部变量,在Java内存模型中没有任何要求将其值暴露给其他线程,除非使用同步原语。或者说:变量done
的值不会被任何代码更改或查看,除了此处所示的代码。
在这种情况下,由于循环不会更改done
的值,因此它的值可以被有效忽略,并且编译器可以将该变量的评估提升到循环外部,防止其在循环的“热”部分进行评估。这使得循环运行速度更快,因为它需要做的工作更少。
这也适用于更复杂的表达式,例如数组的长度:
int[] array = new int[10000];
for (int i = 0; i < array.length; ++i) {
array[i] = Random.nextInt();
}
在这种情况下,朴素的实现会对数组的长度进行10000次求值。但是,由于变量array从未被赋值且数组的长度永远不会改变,因此求值可以改为:int[] array = new int[10000];
for (int i = 0, $l = array.length; i < $l; ++i) {
array[i] = Random.nextInt();
}
这里还有其他与提升无关的优化方法。
希望这能帮到你。
volatile
(原子性和可见性)和synchronized
(原子性、可见性和互斥)用于表达更改的可见性(对其他线程)。但是,此外您还必须了解“先于规则”(请参见Goetz等人的《Java并发实践》第341f页(JCP)和Java语言规范§17)。System.out.println()
时会发生什么?请参见上文。首先,您需要两个System.out.println()
调用。一个在主方法中(在更改done
之后),另一个在启动的线程中(在while
循环中)。现在,我们必须考虑JLS §17中的程序顺序规则和监视器锁定规则。这里是简短版本:您有一个公共锁对象M。在线程A解锁M之前发生的所有事情对于在B锁定M的那一刻另一个线程B是可见的(请参见JCP)。System.out
中的一个公共PrintStream
对象。当我们查看println()
内部时,您会看到调用了synchronized(this)
。System.out.println()
“刷新”变量done
的状态更改。System.out.println("i = " + i);
,那么变量提升将不起作用,这意味着程序会按预期停止。println方法是线程安全的,因此jvm无法优化代码段?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;
}
done
不是易失性变量,即使它是一个实例或类变量,这种优化也可以发生。 - assylias