概述
從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
值了。