您好,登錄后才能下訂單哦!
public class Demo09 {
public static boolean flag = true;
public static class T1 extends Thread {
public T1(String name) {
super(name);
}
@Override
public void run() {
System.out.println("線程" + this.getName() + " in");
while (flag) {
;
}
System.out.println("線程" + this.getName() + "停止了");
}
}
public static void main(String[] args) throws InterruptedException {
new T1("t1").start();
//休眠1秒
Thread.sleep(1000);
//將flag置為false
flag = false;
}
}
運行上面代碼,會發(fā)現(xiàn)程序無法終止。
線程t1的run()方法中有個循環(huán),通過flag來控制循環(huán)是否結(jié)束,主線程中休眠了1秒,將flag置為false,按說此時線程t1會檢測到flag為false,打印“線程t1停止了”,為何和我們期望的結(jié)果不一樣呢?運行上面的代碼我們可以判斷,t1中看到的flag一直為ture,主線程將flag置為false之后,t1線程中沒有看到,所以一直死循環(huán)。
那么t1中為什么看不到被主線程修改之后的flag?
要解釋這個,我們需要先了解一下java內(nèi)存模型(JMM),Java線程之間的通信由Java內(nèi)存模型(本文簡稱為JMM)控制,JMM決定一個線程對共享變量的寫入何時對另一個線程可見。從抽象的角度來看,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:線程之間的共享變量存儲在主內(nèi)存(main memory)中,每個線程都有一個私有的本地內(nèi)存(local memory),本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本。本地內(nèi)存是JMM的一個抽象概念,并不真實存在。它涵蓋了緩存,寫緩沖區(qū),寄存器以及其他的硬件和編譯器優(yōu)化。Java內(nèi)存模型的抽象示意圖如下:
從上圖中可以看出,線程A需要和線程B通信,必須要經(jīng)歷下面2個步驟:
下面通過示意圖來說明這兩個步驟:
如上圖所示,本地內(nèi)存A和B有主內(nèi)存中共享變量x的副本。假設(shè)初始時,這三個內(nèi)存中的x值都為0。線程A在執(zhí)行時,把更新后的x值(假設(shè)值為1)臨時存放在自己的本地內(nèi)存A中。當線程A和線程B需要通信時,線程A首先會把自己本地內(nèi)存中修改后的x值刷新到主內(nèi)存中,此時主內(nèi)存中的x值變?yōu)榱?。隨后,線程B到主內(nèi)存中去讀取線程A更新后的x值,此時線程B的本地內(nèi)存的x值也變?yōu)榱?。
從整體來看,這兩個步驟實質(zhì)上是線程A在向線程B發(fā)送消息,而且這個通信過程必須要經(jīng)過主內(nèi)存。JMM通過控制主內(nèi)存與每個線程的本地內(nèi)存之間的交互,來為java程序員提供內(nèi)存可見性保證。
對JMM了解之后,我們再看看文章開頭的問題,線程t1中為何看不到被主線程修改為false的flag的值,有兩種可能:
對于上面2種情況,有沒有什么辦法可以解決?
是否有這樣的方法:線程中修改了工作內(nèi)存中的副本之后,立即將其刷新到主內(nèi)存;工作內(nèi)存中每次讀取共享變量時,都去主內(nèi)存中重新讀取,然后拷貝到工作內(nèi)存。
java幫我們提供了這樣的方法,使用volatile修飾共享變量,就可以達到上面的效果,被volatile修改的變量有以下特點:
我們修改一下開頭的示例代碼:
public volatile static boolean flag = true;
使用volatile修飾flag變量,然后運行一下程序,輸出:
線程t1 in
線程t1停止了
這下程序可以正常停止了。
volatile解決了共享變量在多線程中可見性的問題,可見性是指一個線程對共享變量的修改,對于另一個線程來說是否是可以看到的。
java高并發(fā)系列連載中,總計估計會有四五十篇文章,可以關(guān)注公眾號:javacode2018,獲取最新文章。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。