我们网上学的基本都是以下结论:
内存模型是一个抽象模型,主要包括了线程私有的本地内存与本地内存里的共享变量副本、主内存共有的共享变量
可见性问题:当线程对本地共享变量的修改操作,多线程下可能无法保证共享变量及时刷回主存,使得所有其他线程可见(volatile)
package com.special.thread.volatiletest;
/**
* 首先执行main方法的时候;
* 会创建出一个线程t1;
* 这个线程t1会来进行读取共享变量flag的取值;
* 则读取到flag共享变量的取值为true;
* 那么该while循环则将会一直进行循环;
* 那么当主线程沉睡了0.1秒之后,结束;
* 但是控制台显示,t1并没有结束;
*
* 如果加上volatile则会结束
*
*
*/
public class VolatileDemo {
static volatile boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (!flag) {
//
}
});
t1.start();
Thread.sleep(1000);
flag = true;
System.out.println("main end");
}
}
上述说法其实是错误的理论
被证实是错误的说法:通过循环判断一个值变为TRUE就停止循环,读不到TRUE的原因是因为读取的此处太多次100w,大概一个阈值,使其被
JIT优化
,导致其不再读取主存的值,默认为FALSE。所以TRUE的改变他获取不到。证实方法:将修改为TRUE的时间加快,睡眠时间设为1,就可以发现他可以正常获取TRUE并停止。(还没有被jit优化)
package com.special.thread.volatiletest;
public class VolatileDemo {
static volatile boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (!flag) {
//
}
});
Thread t2 = new Thread(() -> {
Thread.sleep(1000);
System.out.println(flag);
});
t1.start();
t2.start();
Thread.sleep(1000);
flag = true;
System.out.println("main end");
}
}
这时我们开多一个线程,可以发现可以读取到flag的最新的值TRUE,但是线程1循环依旧没有停止。
package com.special.thread.volatiletest;
public class VolatileDemo {
static volatile boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (!flag) {
//
}
});
Thread t2 = new Thread(() -> {
Thread.sleep(1000);
System.out.println(flag);
});
t1.start();
t2.start();
Thread.sleep(1);
flag = true;
System.out.println("main end");
}
}
如果我们将主线程的睡眠时间缩短,更快的修改flag的值为TRUE,会发现循环停止了。
所以并不是因为新的值没有及时刷回主存,导致循环无法停止,而是jit优化,因为重复读取了上百万次flag,jvm发现都是同一个值,所以直接对他优化。
Thread t1 = new Thread(() -> {
while (!FALSE) { //直接将flag替换为FALSE,就不会再去读取flag的值
//
}
});