概述
从JMM
层面理解多线程对共享变量修改时的可见性问题。
源码
/**
* <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关键字
private static volatile boolean flag = false;
加了volatile后的执行结果
[1号线程]启动
[2号线程]当前标记为:[false]
[2号线程]修改后标记为:[true]
[1号线程]执行标记修改后的代码
[1号线程]结束
结论
为何不加volatile
关键字时,[1号线程]读取不到[2号线程]对共享变量flag
的修改呢?
静态变量,刚开始是在主内存
中。当两个线程都使用到flag
时,jvm
会把这个变量复制到各自的工作内存
中存一份副本。
[2号线程]只是把自己工作内存种的副本给修改了,这时[1号线程]当然不知道flag
值已经修改了。
volatile 强制变量的赋值会同步刷新回主内存,强制变量的读取会从主内存重新加载,保证不同的线程总是能够看到该变量的最新值。
加了volatile
之后,[2号线程]修改了flag
值之后,会刷新回主内存
,[1]号线程读取时,从主内存
重新加载,这时[1号线程]就读取到修改后的flag
值了。