这里是Themberfue
· 在上一节中,我们讨论了死锁的概念,产生的场景 ,产生的必要条件......
内存可见性
· 我们先来看一段百度百科关于 "内存可见性" 的解释
· "内存可见性" 就是造成线程安全问题的原因之一
· 如果是单纯地看这段文字,可能会比较难读,我们通过一段代码来深入理解这个问题
public class Demo21 {
public static int flag = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (flag == 0) {
}
System.out.println("t1线程结束");
});
Thread t2 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
flag = 1;
System.out.println("t2线程结束");
});
t1.start();
t2.start();
t1.join();
t2.join();
}
}
· 这段不难读懂,t1 线程判断变量 flag 的改变情况,若为 0,则 t1 线程循环执行,若被修改后,则 t1 线程会立刻终止执行
· t2 线程执行修改变量 flag 的任务,但并非立刻执行修改的逻辑,而是1ms 后
· 这样的代码理论上并无问题,在 1s 后,两个线程都会结束
· 但实际运行后,你会发现虽然 t2 线程结束了,但是 t1 线程却迟迟没有结束,查看变量 flag 的值,也确实更改为了 1,但为什么 t1 线程没有结束呢?
· 众所周知,程序员之间的水平都是参差不平的
· 优化代码这件事本来就是很微妙的,程序都能跑,那我就懒得优化了,这绝对是大多数程序员的写照(但是,代码优化也是非常重要的事情,还是要注意的)
· 研究 JDK 的大佬们,就希望通过 编译器 或者 JVM 来给我们写出的代码自动优化,在对逻辑不变的前提下,对代码的结构进行一些小幅度的调整从而使代码更加高效的运行
· 这样的事看起来当然好,但是难免地会引发弊端,尤其是在编写多线程代码时,编译器 或者 JVM 的优化可能就会判断失误,导致逻辑理论上是正确的,但结果却大不尽人如意
· "内存可见性" 就是由于 JVM 自动优化代码所造成的
· 我们先来了解汇编语言中的一个指令:"cmp" 本质是比较(compare)指令,通常用于控制程序流程(例如条件判断和分支),cmp 本质是一个隐式减法操作
while (flag == 0) {}
· 上述伪代码就是一个条件判断,在执行 "cmp" 指令之前,程序还得先执行 load 指令(在x86架构中是用 mov 指令执行类似的操作),