下面这段代码,按正常逻辑打印完"[flag = true] end"日志后,程序就会正常退出了。但实际运行,程序未能正常退出,因为while循环没有退出。
private static boolean flag;
public static void main(String[] args) throws Exception {
Thread t = new Thread(() -> {
int i = 0;
while (!flag) {
i++;
}
});
t.start();
TimeUnit.SECONDS.sleep(1);
log.debug("[flag = true] start");
flag = true;
log.debug("[flag = true] end");
}
那怎么让程序正常退出?
方法一:在给静态变量flag添加volatile关键字,然后运行程序可以正常退出。
private static volatile boolean flag;
那这个问题是不是就是可见性问题呢?
方法二:不用volatile,在while循环体添加打印i++,然后运行程序可以正常退出。
while (!flag) {
// i++;
System.out.println(i++); //或者log.debug("{}", i++);
}
我们用方法二证明了这并非是volatile可见性的问题。
根本原因是Java C2编译优化惹的祸,
while (!flag) {
i++;
}
// 上面这段代码在编辑期被编译成了
if (!flag) {
while (true) {
i++;
}
}
所以下面修改flag=true时,就跟while条件没关系了,因为while条件始终为true。
除了Java编译优化,我在另一篇文章里介绍了Java的指令重排,也是JVM对应用程序的一种优化。