主线程开子线程
boolean flag=true;
public static void main(String[] args) throws InterruptedException {
TestThread t = new TestThread();
t.start();//开子线程 1
t.flag = false;//主线程继续跑 2
//绝大部分情况下都是2先执行,所以下面的输出语句不会执行
}
private void start() {
new Thread(new Runnable() {
@Override
public void run() {
while(flag){
System.out.println("sub thread");
}
}
}).start();
}
JIT激进优化产生的可见性问题
boolean flag=true;
public static void main(String[] args) throws InterruptedException {
TestThread t = new TestThread();
t.start();//开子线程 1
Thread.sleep(1000);
t.flag = false;//主线程继续跑 2
//绝大部分情况下都是2先执行,所以下面的输出语句不会执行
}
private void start() {
new Thread(new Runnable() {
@Override
public void run() {
while(flag){
System.out.println("sub thread");
}
}
}).start();
}
在休眠1s后再把flag置为false;结果是程序打印了1s的sub Thread,这一点符合我们的正常逻辑。
重点来了,如果我们把 System.out.println(“sub thread”);这条语句删除,那么这段程序永远不会停止。
这就令人很费解
见下面的程序
boolean flag=true;
public static void main(String[] args) throws InterruptedException {
TestThread t = new TestThread();
t.start();//开子线程 1
Thread.sleep(1000);
t.flag = false;//主线程继续跑 2
//绝大部分情况下都是2先执行,所以下面的输出语句不会执行
}
private void start() {
new Thread(new Runnable() {
@Override
public void run() {
while(flag){
//空轮询,永远不会停止
}
}
}).start();
}
问题分析:既然一直空轮询,那么可以确定的是子线程的flag一直是true,即使主线程已经修改为false。
什么原因导致flag一直是true,我先列举了一系列可能的原因如下:
原因一:主线程的flag对子线程不可见,加volatile关键字解决。
这个原因可以排除掉,就算我们的flag不是volatile的,但是总有一个时刻子线程会发现flag被改为flase了吧,不可能一直发现不了啊!并且加了打印语句1s过后程序停止,说明绝不是这个原因。
原因二:JVM晚期优化?
对于上面的空轮询,他认为是可以优化的,类似优化成下面的样子:
把flag暂存起来,这样就不用每次都去堆里面找。这样temp就一直都是true。
private void start() {
new Thread(new Runnable() {
boolean temp = flag;
@Override
public void run() {
while(temp){
//空轮询,永远不会停止
}
}
}).start();
}
而对于再while里面加了打印语句(打印也是方法调用)或者一些方法调用JIT不敢做优化。
上面这些我不敢保证绝对正确,毕竟我不知道怎么可以验证JIT确实对我的空轮询做了晚期优化,这些都只是猜测。
深入理解java虚拟机11章节 深入讲了JIT晚期优化,看的很晕,毕竟基本纯理论,很难验证。