public class demo1 {
private static int count=0;
public static void main(String[] args) {
Thread t1=new Thread(()->{
while(count==0){
}
System.out.println("t1线程结束");
});
Thread t2=new Thread(()->{
Scanner scanner=new Scanner(System.in);
System.out.print("请输入一个整数:");
count= scanner.nextInt();
});
t1.start();
t2.start();
}
}
通过上述代码,我们可以发现预期结果是线程t2在输入一个非0的数字后程序会结束,但实际运行结果是不会结束,卡住了
分析一下为什么会导致这种结果出现:
在执行循环体的时候会将成员变量加载到cpu中,而从内存读取数据到cpu相对于cpu中的cmp来说执行速度是非常慢的,所以在cmp执行很多次后感受到cmp的内容并没有变化,于是就发生了编译器优化,只是在第一次进行了真正的load,后续代码执行到t2时就不再去load,用的就是第一次load的值。
如何解决内存可见性问题:
1.使用关键字volatile,
volatile的注意事项:
1.只可以对变量进行修饰,不可以对方法进行修饰。
2.不可以对方法中的局部变量进行修饰
private volatile static int count=0;
这个关键字就是告诉编译器,不要触发上述优化(具体在java中,是让javac生成字节码的时候产生了“内存屏障”相关指令)
jvm中上述问题是这么表达的:
当t1执行的时候,要从工作内存中读取count的值,而不是从主内存中
后续t2修改count,也是会先修改工作内存(工作存储区(cpu寄存器+缓存)),同步拷贝到主内存。但是由于t1没有重新读取主内存,导致最终t1没有感知到t2的修改
synchronized(锁)和volatile的理解:
首相这两者是没有任何关系的,对于sychronized是将代码中的非原子性指令打包成一个整体,防止由于线程之间相互竞争特性而导致指令的执行顺序发生错误,导致代码bug。而volatile是告诉编译器,此处不要进行优化防止代码优化而出现bug。