Java 記憶體模型

ACatSmiling發表於2024-10-02

Author: ACatSmiling

Since: 2024-07-24

概念

Java 記憶體模型Java Memory Model,簡稱 JMM,是 Java 語言中定義的一組規則和規範,用於解決多執行緒環境下的記憶體可見性和有序性問題。JMM 確定了執行緒之間如何透過記憶體進行互動,並規定了變數的讀取和寫入操作的行為。

JMM 能幹嗎?

  • 透過 JMM 來實現執行緒的工作記憶體和主記憶體之間的抽象關係。
  • 遮蔽各個硬體平臺和作業系統的記憶體訪問差異,以實現讓 Java 程式在各種平臺下都能達到一致性的記憶體訪問效果。

抽象模型

image-20240724224217335

JMM 規定了所有的變數都儲存在主記憶體中,每個執行緒都有自己的工作記憶體,執行緒自己的工作記憶體中儲存了該執行緒使用到的主記憶體變數的副本複製,執行緒對變數的所有操作(讀取、賦值等)都必須線上程自己的工作記憶體中進行,而不能夠直接寫入主記憶體中的變數,不同執行緒之間也無法直接訪問對方工作記憶體中的變數,執行緒間變數值的傳遞均需要透過主記憶體來完成。

  • 主記憶體(Main Memory):所有變數都儲存在主記憶體中,主記憶體是所有執行緒共享的區域。
  • 工作記憶體(Working Memory):每個執行緒都有自己的工作記憶體(類似於 CPU 快取),執行緒在工作記憶體中對變數進行操作。工作記憶體中的變數是主記憶體中變數的複製,執行緒對變數的修改必須在某個時刻重新整理回主記憶體。

三大特性

記憶體可見性:JMM 規定了一個執行緒對共享變數的修改何時對另一個執行緒可見,在沒有適當的同步機制時,執行緒可能會看到舊的、不一致的資料。

原子性:JMM 確保基本的讀寫操作是原子的(不可分割的)。

有序性:JMM 規定了程式中指令的執行順序,編譯器和處理器可能會對指令進行重排序,但 JMM 規定了哪些重排序是可見的,哪些是不可見的,以確保某些操作不會被重排序而破壞程式的正確性。

可見性規則

為了確保多執行緒程式設計中的可見性和有序性,JMM 定義了一些關鍵的同步原語和規則:

  1. volatile 變數
    • 對 volatile 變數的讀寫操作具有可見性和有序性。
    • 當一個執行緒修改了 volatile 變數,新的值會立即被重新整理到主記憶體中,其他執行緒讀取時會直接從主記憶體中讀取。
  2. synchronized 塊
    • synchronized 塊可以確保進入同步塊的執行緒對共享變數的修改對其他執行緒可見。
    • 每個物件都有一個監視器鎖(monitor lock),執行緒透過獲取鎖來實現同步。
  3. final 變數
    • final 變數在建構函式結束後不能被修改,且在建構函式中對 final 變數的寫入對其他執行緒可見。

happens-before 原則

happens-before 原則happens-before 關係定義了一個操作的結果對另一個操作可見的條件,可以用於確定多個操作之間的順序性和可見性,進而確保了多執行緒程式中的記憶體一致性和正確性。透過遵循這些規則,開發者可以確保在多執行緒環境中讀寫共享變數時不會出現意外的行為。

happens-before 原則內容如下:

  1. 程式次序規則(Program Order Rule)在一個執行緒內,按照程式的順序,前面的操作 happens-before 後面的操作。例如,在同一個執行緒中,a = 1; b = 2;,則 a = 1 happens-before b = 2。
  2. 監視器鎖規則(Monitor Lock Rule)對一個鎖的解鎖操作 happens-before 其後的對這個鎖的加鎖操作。例如,執行緒 A 對某個物件的解鎖操作 happens-before 執行緒 B 對同一個物件的加鎖操作。
  3. volatile 變數規則(Volatile Variable Rule)對一個 volatile 變數的寫操作 happens-before 後續對這個 volatile 變數的讀操作。例如,執行緒 A 對 volatile 變數 x 的寫操作 x = 1 happens-before 執行緒 B 對 x 的讀操作 int y = x。
  4. 執行緒啟動規則(Thread Start Rule)在主執行緒中對執行緒物件的啟動操作 happens-before 啟動執行緒中的每一個操作。例如,主執行緒呼叫 thread.start() happens-before 新執行緒中的任何操作。
  5. 執行緒中斷規則(Thread Interruption Rule)對執行緒物件的中斷操作 happens-before 被中斷執行緒檢測到中斷事件的發生。例如,主執行緒呼叫 thread.interrupt() happens-before 被中斷執行緒檢測到中斷(透過 Thread.interrupted() 或 Thread.isInterrupted())。
  6. 執行緒終止規則(Thread Termination Rule)一個執行緒中的所有操作 happens-before 另一個執行緒檢測到這個執行緒已經終止或等待這個執行緒終止。例如,執行緒 A 中的所有操作 happens-before 主執行緒檢測到執行緒 A 已終止(透過 thread.join())。
  7. 物件構造規則(Object Construction Rule)物件的建構函式的執行 happens-before 該物件的 finalize() 方法的開始。例如,某個物件的建構函式執行完畢 happens-before 該物件的 finalize() 方法開始。
  8. 傳遞性(Transitivity)如果 A happens-before B,且 B happens-before C,那麼 A happens-before C。例如,如果 a = 1 happens-before b = 2,且 b = 2 happens-before c = 3,那麼 a = 1 happens-before c = 3。

原文連結

https://github.com/ACatSmiling/zero-to-zero/blob/main/JavaLanguage/java-util-concurrent.md

相關文章