你好,這裡是codetrend專欄“高併發程式設計基礎”。
點選合集可以檢視往期文章。
什麼是JMM
Java記憶體模型(JMM)是Java語言規範的一部分,定義了多執行緒環境下共享變數的訪問規則。它解決了以下主要問題:
- 可見性:確保一個執行緒對共享變數的修改能夠被其他執行緒看到。
- 原子性:保證某些操作的不可分割性。
- 有序性:避免由於編譯器和處理器最佳化而導致的指令重排序問題。
JMM透過“happens-before”規則和記憶體屏障等機制,確保在多執行緒程式中,各執行緒對共享變數的操作行為符合預期。
CPU設計決定JMM設計
複雜的CPU設計需要設計Java記憶體模型(JMM)的原因包括:
指令重排序:
- CPU和編譯器可能會對指令進行重排序以最佳化效能,這可能導致不同執行緒看到的操作順序不一致。
快取一致性:
- 多核CPU的每個核心可能有自己的快取,JMM確保不同核心之間的快取一致性,使得一個執行緒的修改能被其他執行緒看到。
記憶體屏障:
- 為了確保執行緒操作的正確順序,需要在指令之間插入記憶體屏障。JMM定義了這些屏障的行為,確保記憶體操作的可見性和順序性。
最佳化與併發:
- 現代CPU進行各種最佳化(如亂序執行),JMM提供了規則以保證這些最佳化不會破壞多執行緒程式的正確性。
graph LR
A[複雜 CPU 設計] --> B[指令重排序]
A --> C[快取一致性]
A --> D[記憶體屏障]
A --> E[最佳化與併發]
B --> F[可能導致操作順序不同]
C --> G[確保多核快取一致]
D --> H[定義記憶體操作順序]
E --> I[保證最佳化不破壞正確性]
CPU設計和JMM的聯絡
CPU設計和Java記憶體模型(JMM)在多執行緒程式中的角色雖然相關,但它們關注的層面和目標有所不同。
graph LR
A[CPU設計] --> B[快取一致性]
A --> C[指令重排序]
A --> D[記憶體屏障]
A --> E[多執行緒支援]
A --> F[硬體最佳化]
B --> G[快取一致性協議]
C --> H[重排序規則]
D --> I[記憶體屏障機制]
E --> J[硬體同步機制]
F --> K[效能最佳化]
L[JMM] --> M[可見性]
L --> N[原子性]
L --> O[有序性]
L --> P[happens-before規則]
M --> Q[執行緒間可見]
N --> R[操作的不可分割性]
O --> S[操作順序保證]
P --> T[程式行為規範]
A --> U[與JMM的關係]
U --> L
U --> V[硬體支援JMM]
V --> Q
V --> R
V --> S
V --> T
聯絡
多執行緒同步:
- JMM:定義了在Java程式中執行緒之間共享變數的可見性、原子性和有序性,確保多執行緒程式的正確性。
- CPU設計:提供硬體支援,以確保不同執行緒間的操作一致性,可能透過快取一致性協議、記憶體屏障等方式來實現。
記憶體一致性:
- JMM:透過“happens-before”規則確保執行緒操作的可見性和順序性。
- CPU設計:實現快取一致性協議(如MESI協議)來保證不同核心或執行緒對記憶體的檢視一致。
指令重排序:
- JMM:規定了編譯器和處理器在執行執行緒操作時的行為,避免由於重排序導致的錯誤結果。
- CPU設計:允許指令重排序以提高效能,並透過記憶體屏障和同步機制確保重排序不會破壞程式的正確性。
區別
關注層面:
- JMM:關注於Java程式中的執行緒間記憶體操作的語義,是一種抽象的記憶體模型,描述了Java虛擬機器和程式設計師之間的行為規範。
- CPU設計:關注於硬體級別的實現細節,如處理器架構、快取管理、管道化等,是具體的硬體設計和最佳化。
實現方式:
- JMM:透過Java語言規範和Java虛擬機器的實現來保證執行緒的正確性,不直接涉及硬體實現細節。
- CPU設計:涉及具體的硬體設計和微架構實現,如快取一致性協議、記憶體屏障等,以支援多執行緒操作的正確性和高效性。
抽象層次:
- JMM:在軟體層面上定義行為,主要面向程式設計師和Java虛擬機器的實現者,抽象了硬體的細節。
- CPU設計:在硬體層面上定義操作,涉及具體的電路和硬體實現,直接影響效能和功能。
JMM帶來的好處
JMM的設計作為一個統一入口,減少了開發工作量,包括相容、效能這些都處理。
graph LR
A[JMM的好處] --> B[提高程式的可移植性]
A --> C[保證多執行緒程式設計的正確性]
A --> D[便於開發人員理解和使用]
A --> E[最佳化效能]
B --> F[統一的記憶體模型]
B --> G[無需或少量修改]
B --> H[跨平臺相容]
C --> I[記憶體可見性]
C --> J[操作原子性]
C --> K[順序一致性]
C --> L[同步機制]
D --> M[簡化記憶體訪問行為]
D --> N[易於理解同步機制]
D --> O[提高開發效率]
E --> P[最佳化鎖競爭]
E --> Q[合理使用 volatile]
E --> R[編譯器和處理器最佳化]
- 提高程式的可移植性:由於 JMM 提供了一個統一的記憶體模型,Java 程式可以在不同的硬體平臺和作業系統上無需修改或只需少量修改就能夠正確執行。這大大提高了 Java 程式的可移植性,使得開發人員可以更加專注於業務邏輯的實現,而不必擔心底層硬體的差異。
- 保證多執行緒程式設計的正確性:JMM 定義了一系列規則來保證多執行緒環境下記憶體的可見性、原子性和有序性。這使得開發人員可以使用 Java 提供的同步機制(如 synchronized、volatile 等)來確保多執行緒程式的正確性,避免了由於硬體特性和編譯器最佳化導致的難以除錯的錯誤。
- 便於開發人員理解和使用:JMM 提供了一個相對簡單和直觀的記憶體模型,開發人員可以更容易地理解多執行緒程式設計中的記憶體訪問行為和同步機制。這有助於提高開發效率,減少錯誤的發生。
- 最佳化效能:雖然 JMM 對記憶體訪問進行了一定的限制,但同時也提供了一些最佳化的機會。例如,合理地使用 volatile 關鍵字和原子類可以避免不必要的鎖競爭,提高程式的效能。此外,JMM 也允許編譯器和處理器在不影響程式正確性的前提下進行一些最佳化,如指令重排序等,以提高程式的執行效率。
JMM的發展歷程
Java 1.0 和 1.1:
- 初期版本沒有明確的記憶體模型,執行緒安全和同步的定義較為鬆散。
Java 1.2:
- 引入了
synchronized
關鍵字和volatile
變數,初步定義了多執行緒的基本同步機制。
Java 1.5 (2004):
- 引入了 Java 記憶體模型(JMM)的正式規範。增加了
java.util.concurrent
包,提供了更豐富的併發工具和原子變數。 - 確立了 JMM 的基本規則,如
happens-before
關係、可見性、原子性和有序性。
Java 1.6 到 1.8:
- 在 JMM 的基礎上,增加了更多併發工具和最佳化機制,如
ConcurrentHashMap
和ForkJoinPool
。 - 對 JMM 進行了效能最佳化和補充,如加強了對編譯器和處理器最佳化的支援。
Java 9 到 11:
- 引入了新的特性如
CompletableFuture
和增強的Stream API
,進一步提高了併發程式設計的效率和簡易性。
Java 12 及以後:
- 持續最佳化和補充併發程式設計相關的特性和工具,如
Record
型別和Pattern Matching
,並不斷調整 JMM 以適應新的程式設計模式。
JMM一直在不斷髮展以適應不同的設計和硬體。
進一步增強效能:
- 透過硬體支援和編譯器最佳化,繼續提高 JMM 在多執行緒環境下的效能表現。
適應新程式設計模型:
- 支援新興的程式設計模型和併發模式,例如響應式程式設計和協程(輕量級執行緒)。
簡化開發和除錯:
- 提供更高層次的抽象和工具,以簡化併發程式設計的開發和除錯過程。
跨平臺一致性:
- 確保 JMM 在不同硬體平臺和 JVM 實現中的一致性,以提高跨平臺的相容性和可靠性。
增強文件和教育:
- 提供更全面的文件和教育資源,幫助開發者更好地理解和應用 JMM。
關於作者
來自一線全棧程式設計師nine的探索與實踐,持續迭代中。
歡迎關注、評論、點贊。