概述
从
JMM
层面理解多线程对共享变量修改时的可见性问题。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiIXZ05WZj91YpB3IwczX0xiRGZkRGZ0Xy9GbvNGL2EzXlpXazxSPJRVT4tGVZFDayoVdWdlYwhnMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL1gDNyUzMxETMwMTNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
源码
/**
* <pre>
* 测试两个线程,对同一个局部变量进行修改,是否可见
* 1、启动[1号线程],启动后,等待flag值变为true,则继续执行、然后退出循环
* 2、启动[2号线程],启动后,该线程会把flag值改成true
* 3、观察输出,看[2号线程]修改flag后,[1号线程]是否可以感知到。
* 如果[1号线程]继续执行了,则表明读到了修改后的值;如果没有执行,说明没有读到修改后的值。
* 关键字:volatile
* </pre>
* created at 2020-05-30 08:26
* @author lerry
*/
public class VolatileDemo {
private static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
System.out.printf("[1号线程]启动\n");
while (true) {
if (flag) {
System.out.printf("[1号线程]执行标记修改后的代码\n");
break;
}
}
System.out.printf("[1号线程]结束\n");
});
thread1.start();
int seconds = 1;
Thread.sleep(seconds * 1000);
new Thread(() -> {
System.out.printf("[2号线程]当前标记为:[%s]\n", flag);
flag = true;
System.out.printf("[2号线程]修改后标记为:[%s]\n", flag);
}).start();
}
}
执行结果
[1号线程]启动
[2号线程]当前标记为:[false]
[2号线程]修改后标记为:[true]
结论
[2号线程]修改`flag`值后,[1号线程]并不知道
加上volatile关键字
加了volatile后的执行结果
[1号线程]启动
[2号线程]当前标记为:[false]
[2号线程]修改后标记为:[true]
[1号线程]执行标记修改后的代码
[1号线程]结束
结论
为何不加
volatile
关键字时,[1号线程]读取不到[2号线程]对共享变量
flag
的修改呢?
静态变量,刚开始是在
主内存
中。当两个线程都使用到
flag
时,
jvm
会把这个变量复制到各自的
工作内存
中存一份副本。
[2号线程]只是把自己工作内存种的副本给修改了,这时[1号线程]当然不知道
flag
值已经修改了。
volatile 强制变量的赋值会同步刷新回主内存,强制变量的读取会从主内存重新加载,保证不同的线程总是能够看到该变量的最新值。
加了
volatile
之后,[2号线程]修改了
flag
值之后,会刷新回
主内存
,[1]号线程读取时,从
主内存
重新加载,这时[1号线程]就读取到修改后的
flag
值了。