一个问题引发的思考
public class ThreadProblem {
static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
int i=0;
while(!stop){
i++;
}
});
thread.start();
Thread.sleep(1000);
stop=true;
}
}
预期1s后程序退出,为啥没有呢? stop对线程不可见造成的?
在i++后加入打印语句可以顺利结束?
jvm server版本hotspot的JIT对指令优化的结果,这里分为两个层面来解答
println底层用到了synchronized这个同步关键字,这个同步会防止循环期间对于stop值的缓存。因为println有加锁的操作,而释放锁的操作,会强制性的把工作内存中涉及到的写操作同步到主
内存,可以通过如下代码去证明。
public class ThreadProblem {
volatile static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
int i=0;
while(!stop){
i++;
synchronized (ThreadProblem.class){
}
}
});
thread.start();
Thread.sleep(1000);
stop=true;
}
}
第三个角度,从IO角度来说,print本质上是一个IO的操作,我们知道磁盘IO的效率一定要比CPU的计算效率慢得多,所以IO可以使得CPU有时间去做内存刷新的事情,从而导致这个现象。比如我们可以在里面定义一个new File("xxx.txt")。同样会达到效果.
Thread.sleep(0)也可结束程序退出?
这个我认为是和cpu、以及jvm、操作系统等因素 有关系。 官方文档上是说,Thread.sleep没有任何同步语义,编译器不需要在调用Thread.sleep之前把缓存在寄 存器中的写刷新到给共享内存、也不需要在Thread.sleep之后重新加载缓存在寄存器中的值。 编译器可以自由选择读取stop的值一次或者多次,这个是由编译器自己来决定的。 但是Thread.sleep(0)导致线程切换,线程切换会导致缓存失效从而读取到了新的值。
对stop变量使用volatile关键字可以解决线程间的可见性问题。
总线锁重量级的 先尝试使用缓存锁
cpu 内存 io 速度不一致出现了 MESI 协议 --》 store Buffer --》指令重排序-》内存屏障