首頁 >Java >java教程 >java高併發的volatile與Java記憶體模型是什麼

java高併發的volatile與Java記憶體模型是什麼

PHPz
PHPz轉載
2023-04-30 23:46:151574瀏覽

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;	
    }	
}

執行上面程式碼,會發現程式無法終止。

線程t1的run()方法中有個循環,透過flag來控制循環是否結束,主執行緒中休眠了1秒,將flag置為false,按說此時執行緒t1會偵測到flag為false,列印“線程t1停止了”,為何和我們期望的結果不一樣呢?運行上面的程式碼我們可以判斷,t1中看到的flag一直是true,主執行緒將flag置為false之後,t1執行緒中並沒有看到,所以一直死迴圈。

那麼t1中為什麼看不到被主執行緒修改之後的flag?

要解釋這個,我們需要先了解java記憶體模型(JMM),Java執行緒之間的通訊由Java記憶體模型(本文簡稱為JMM)控制,JMM決定一個執行緒對共享變數的寫入何時對另一個線程可見。從抽象的角度來看,JMM定義了線程和主記憶體之間的抽象關係:線程之間的共享變數儲存在主記憶體(main memory)中,每個執行緒都有一個私有的本地記憶體(local memory) ,本地記憶體中儲存了該線程以讀/寫共享變數的副本。本地記憶體是JMM的一個抽象概念,並不真實存在。它涵蓋了緩存,寫入緩衝區,寄存器以及其他的硬體和編譯器最佳化。 Java記憶體模型的抽象示意圖如下:

java高併發的volatile與Java記憶體模型是什麼

#從上圖可以看出,執行緒A需要和執行緒B通信,必須要經歷下面2個步驟:

1.首先,線程A把本地內存A中更新過的共享變數刷新到主內存中去

2.然後,線程B到主內存中去讀取線程A之前已更新過的共享變數

下面透過示意圖來說明這兩個步驟:

java高併發的volatile與Java記憶體模型是什麼

#如上圖所示,本地記憶體A和B有主記憶體中共享變量x的副本。假設初始時,這三個記憶體中的x值都為0。線程A執行時,把更新後的x值(假設值為1)暫時存放在自己的本機記憶體A中。當線程A和線程B需要通訊時,線程A首先會把自己本地記憶體中修改後的x值刷新到主記憶體中,此時主記憶體中的x值變成1。隨後,線程B到主記憶體中去讀取線程A更新後的x值,此時線程B的本地記憶體的x值也變成了1。從整體來看,這兩個步驟實質上是線程A在向線程B發送訊息,而且這個通訊過程必須要經過主記憶體。 JMM透過控制主記憶體與每個執行緒的本地記憶體之間的交互,來為java程式設計師提供記憶體可見性保證。

對JMM了解之後,我們再看看文章開頭的問題,在線程t1中為何看不到被主線程修改為false的flag的值,有兩種可能:

1 .主線程修改了flag之後,未將其刷新到主內存,所以t1看不到

2.主線程將flag刷新到了主內存,但是t1一直讀取的是自己工作內存中flag的值,沒有去主記憶體中取得flag最新的值

#對於上面2種情況,有沒有辦法可以解決?

是否有這樣的方法:線程中修改了工作記憶體中的副本之後,立即將其刷新到主記憶體;工作記憶體中每次讀取共享變數時,都去主記憶體中重新讀取,然後拷貝到工作記憶體。

java幫我們提供了這樣的方法,使用volatile修飾共享變量,就可以達到上面的效果,被volatile修改的變量有以下特點:

1.線程中讀取的時候,每次讀取都會去主記憶體中讀取共享變數最新的值,然後將其複製到工作記憶體

#2.線程中修改了工作記憶體中變數的副本,修改之後會立即刷新到主記憶體

我們修改一下開頭的範例程式碼:

public volatile static boolean flag = true;

使用volatile修飾flag變量,然後執行一下程序,輸出:

线程t1 in	
线程t1停止了

這下程式可以正常停止了。 volatile解決了共享變數在多執行緒中可見性的問題,可見性是指一個執行緒對共享變數的修改,對於另一個執行緒來說是否是可以看到的。 \

以上是java高併發的volatile與Java記憶體模型是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除