什麼是Java記憶體模型(JMM)中的主記憶體和本地記憶體?

setevn發表於2024-07-30

Java記憶體模型(Java Memory Model, JMM)是Java虛擬機器(JVM)規範的一部分,它定義了多執行緒環境下變數訪問的規則和記憶體可見性。在JMM中,主記憶體和本地記憶體是兩個核心概念,它們對於理解Java多執行緒程式設計中的記憶體訪問和同步機制至關重要。

主記憶體

  • 定義:主記憶體是所有執行緒共享的變數儲存區域,是Java堆記憶體中的一部分。它儲存了Java程式中所有的共享變數和物件例項。
  • 作用:主記憶體是執行緒間通訊的橋樑,當執行緒需要訪問某個共享變數時,它首先會嘗試從主記憶體中讀取該變數的值;當執行緒修改了某個共享變數的值後,這個新值也會被寫回到主記憶體中,以便其他執行緒能夠訪問到最新的資料。

本地記憶體(也稱為工作記憶體)

  • 定義:本地記憶體是每個執行緒私有的記憶體區域,它是JMM的一個抽象概念,並不真實存在,它涵蓋了快取、寫緩衝區、暫存器以及其他的硬體和編譯器最佳化。在邏輯上,本地記憶體中儲存了該執行緒以讀/寫共享變數的複製副本。
  • 作用:由於執行緒之間的直接通訊開銷較大,因此Java採用了基於共享記憶體的通訊方式。當執行緒需要訪問某個共享變數時,它首先會將該變數從主記憶體複製到自己的本地記憶體中,然後在本地記憶體中對變數進行操作。操作完成後,再將變數的值寫回主記憶體中。這種機制減少了執行緒間的直接通訊,但也可能導致資料不一致的問題。

JMM的工作機制

JMM透過主記憶體和本地記憶體的互動來實現執行緒之間的通訊。這個互動過程大致如下:

  1. 讀取和載入:從主記憶體讀取變數,並將其載入到本地記憶體。
  2. 使用和賦值:在本地記憶體中使用和修改變數。
  3. 儲存和寫入:將本地記憶體中修改後的變數值儲存並寫回主記憶體。

volatile關鍵字的作用是確保對變數的所有寫操作都會立即重新整理到主記憶體中,而對變數的所有讀操作都會從主記憶體中讀取。這就保證了變數在各個執行緒之間的可見性。

  1. 雙重檢查鎖定(Double-Checked Locking):

雙重檢查鎖定是一種常用的單例模式實現方式,透過減少同步的開銷來提高效能。

  

public class Singleton {
    private volatile static Singleton instance;
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

  在這個例子中,instance變數被宣告為volatile,確保了在多執行緒環境中instance的可見性和有序性。

2、釋出訂閱模式(Publish-Subscribe Pattern):

在釋出訂閱模式中,多個執行緒需要訂閱某個訊息,並在訊息釋出時接收到通知。透過volatile變數,我們可以確保所有訂閱執行緒都能看到最新的訊息。

public class PublishSubscribe {
    private volatile boolean messageReady = false;
 
    public void publish() {
        messageReady = true;
    }
 
    public void subscribe() {
        while (!messageReady) {
            // 等待訊息
        }
        System.out.println("Message received");
    }
 
    public static void main(String[] args) {
        PublishSubscribe ps = new PublishSubscribe();
 
        Thread publisher = new Thread(() -> ps.publish());
        Thread subscriber = new Thread(() -> ps.subscribe());
 
        subscriber.start();
        publisher.start();
    }
}

  messageReady被宣告為volatile,確保釋出執行緒對messageReady的修改對訂閱執行緒可見。

總結

  • 主記憶體與本地記憶體的關係:主記憶體是共享的,而本地記憶體是私有的。執行緒透過讀寫主記憶體中的共享變數來與其他執行緒進行通訊,但每個執行緒在訪問共享變數時,會先將變數從主記憶體複製到本地記憶體,操作完成後再寫回主記憶體。
  • 資料一致性問題:由於本地記憶體的存在,執行緒A對共享變數的修改並不直接反映到執行緒B的本地記憶體中,因此需要JMM提供機制來保證這種可見性。JMM透過同步機制(如synchronized關鍵字、volatile關鍵字等)來確保共享變數的可見性和有序性。

綜上所述,主記憶體和本地記憶體是Java記憶體模型中的兩個重要概念,它們共同構成了Java多執行緒程式設計中的記憶體訪問和同步機制的基礎。

相關文章