1、先看一段代碼
public class T {
boolean isRunning = true;
public void m() {
System.out.println("m start...");
while(isRunning) {
}
System.out.println("m end...");
}
public static void main(String[] args) {
T t = new T();
new Thread(t::m, "t1").start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.isRunning = false;
}
}
- 首先新建一個線程t1啟動,調用m方法,進入死循環,按道理來說我主線程執行到下面的時候把isRunning改成false就會結束t1線程中的死循環,但是執行你會發現並不會結束。
- 這是因為每個線程運行都會有自己的一份內存也可以叫做緩衝區,比如下面這張圖,上面一個t1線程和主線程分別佔用一個CPU,那麼CPU中會有個對應線程的緩衝區。
- 代碼執行過程:棧內存,堆內存,方法區之類的內存稱之為主內存,堆內存對應的t對象中有個成員變數isRunning=true,在t1線程啟動的時候會從主內存中copy一份isRunning(boolean類型為一個位元組)的值到自己的緩衝區中,然後就開始死循環一直執行下去,這時候主線程把isRunning的值copy到自己的緩衝區然後執行isRunning=false並且寫會主內存中去,但是t1線程是非常繁忙的,一直在執行死循環沒有時間去主內存刷新自己isRunning的值,所以就算主線程修改了isRunning的值,t1線程還是死循環
- 解決方法:在isRunning成員變數前面加上volatile關鍵字
- volatile關鍵字的作用:就是保持線程的可見性,只要該變數的值修改了,就會通知其他線程你們緩衝區中的copy過期了,需要重新來主內存中刷新一下,這時候t1線程就會得到通知,並且來主內存中刷新isRunning的值,從而停止死循環。注意:使用volatile的作用不是t1線程會每次使用到isRunning的時候都去主內存中讀一下isRunning的值,而是主內存中的isRunning被修改了,就會通知所有的使用到isRunning的線程來主內存中刷新一下isRunning的值
- 也可以在死循環中sleep一段時間,或者列印一些語句,使CPU不要太忙,得出空閑就會來主內存中刷新isRunning的值,但是這樣是不穩定的,我怎麼知道你CPU什麼時候空閑,所以該用volatile還是得用volatile