Java中JMM內存模型與happens-before規則詳解

Java內存模型(jmm)通過主內存與工作內存的劃分,以及happens-before規則,確保線程環境下的數據可見性與一致性。jmm規定所有變量存儲在主內存中,線程操作變量需通過私有的工作內存進行復制,而線程間通信必須經由主內存完成。happens-before規則定義了操作間的可見性關系,并非強制執行順序,而是確保前一操作結果對后一操作可見。1. 程序順序規則:同一線程內代碼順序決定happens-before關系;2. 管程鎖定規則:解鎖操作happens-before后續加鎖操作;3. volatile變量規則:寫操作happens-before讀操作;4. 線程啟動規則:start()方法調用happens-before線程內所有操作;5. 線程終止規則:線程內所有操作happens-before終止檢測;6. 線程中斷規則:interrupt()調用happens-before中斷事件檢測;7. 對象finalize規則:構造函數結束happens-before finalize()開始;8. 傳遞性規則:a happens-before b且b happens-before c,則a happens-before c。jmm屏蔽底層硬件差異,提供統一內存訪問模型,使程序員無需關注cpu緩存、指令重排等細節,從而更專注于業務邏輯實現。

Java中JMM內存模型與happens-before規則詳解

Java內存模型(JMM)定義了Java程序中變量的訪問規則,以及在并發環境下如何保證數據的一致性。Happens-before規則是JMM中最重要的概念之一,它定義了操作之間的可見性,確保在多線程環境下,一個操作的結果對另一個操作是可見的,從而避免數據競爭和不確定性。簡單來說,JMM就像一個交通規則,而happens-before就是其中的重要路標,指引著線程安全地訪問共享數據。

Java中JMM內存模型與happens-before規則詳解

解決方案

Java中JMM內存模型與happens-before規則詳解

JMM圍繞著主內存和工作內存展開。所有變量都存儲在主內存中,而每個線程都有自己的工作內存,其中保存了該線程使用到的變量的副本。線程對變量的所有操作(讀取、賦值等)都必須在工作內存中進行,而不能直接讀寫主內存中的變量。線程之間變量值的傳遞需要通過主內存來完成。

立即學習Java免費學習筆記(深入)”;

Java中JMM內存模型與happens-before規則詳解

Happens-before規則并非要求前一個操作必須在后一個操作之前執行,而是要求前一個操作的執行結果對后一個操作可見。這種可見性并不意味著立即同步,而是保證在特定條件下,后一個操作能夠看到前一個操作的結果。

以下是JMM中一些重要的happens-before規則:

  1. 程序順序規則: 在一個線程中,按照程序代碼的執行順序,書寫在前面的操作happens-before書寫在后面的操作。這保證了單線程內的執行順序。

  2. 管程鎖定規則: 對一個鎖的解鎖happens-before后續對這個鎖的加鎖。這意味著釋放鎖的操作對后續獲取鎖的操作可見。

  3. volatile變量規則: 對一個volatile變量的寫操作happens-before后續對這個volatile變量的讀操作。這保證了volatile變量的可見性。

  4. 線程啟動規則: Thread對象的start()方法happens-before此線程中的每一個動作。

  5. 線程終止規則: 線程中的所有操作happens-before對此線程的終止檢測,可以通過Thread.join()方法結束、Thread.isAlive()的返回值等手段檢測到線程已經終止執行。

  6. 線程中斷規則: 對線程interrupt()方法的調用happens-before被中斷線程的代碼檢測到中斷事件的發生,可以通過Thread.interrupted()方法檢測到是否有中斷發生。

  7. 對象finalize規則: 一個對象的初始化完成(構造函數執行結束)happens-before該對象的finalize()方法的開始。

  8. 傳遞性: 如果操作A happens-before操作B,操作B happens-before操作C,那么操作A happens-before操作C。

理解happens-before規則的關鍵在于認識到它定義的是可見性,而不是執行順序。編譯器和處理器可以對代碼進行優化,只要不違反happens-before規則,就不會影響程序的正確性。

為什么需要JMM和happens-before規則?

并發編程中,由于CPU緩存、指令重排序等優化手段,導致線程之間對共享變量的訪問存在可見性問題。如果沒有JMM和happens-before規則的約束,多線程程序可能會出現各種意想不到的錯誤,例如數據不一致、死鎖等。JMM和happens-before規則提供了一種規范,保證了在并發環境下,程序的正確性和可預測性。如果沒有這些規則,編寫可靠的多線程程序將會非常困難。

如何在實際代碼中應用happens-before規則?

在編寫并發代碼時,應該充分利用happens-before規則來保證線程安全。例如,可以使用volatile關鍵字來保證變量的可見性,使用鎖來保證互斥訪問,使用Thread.join()方法來等待線程結束。

// 使用volatile保證變量的可見性 private volatile boolean running = true;  public void stop() {     running = false; }  public void run() {     while (running) {         // 執行任務     } }

在這個例子中,running變量被聲明為volatile,因此對running的寫操作(在stop()方法中)happens-before對running的讀操作(在run()方法中)。這意味著當stop()方法被調用時,run()方法能夠及時看到running變量的變化,從而退出循環

// 使用鎖保證互斥訪問 private final Object lock = new Object(); private int count = 0;  public void increment() {     synchronized (lock) {         count++;     } }

在這個例子中,synchronized關鍵字保證了對count變量的互斥訪問。對lock的解鎖happens-before后續對lock的加鎖,因此increment()方法是線程安全的。

JMM與硬件內存模型有什么區別

JMM是一種抽象的內存模型,它定義了Java程序中變量的訪問規則。而硬件內存模型則是底層硬件的實現,例如CPU緩存、內存總線等。JMM的目標是屏蔽底層硬件的差異,為Java程序員提供一種統一的內存訪問模型。JMM的實現需要考慮底層硬件的限制,例如CPU緩存一致性協議,以保證程序的正確性。可以把JMM看作是Java語言層面對內存訪問的規范,而硬件內存模型則是實際的物理實現。JMM通過一系列規則,將硬件內存模型的復雜性抽象出來,使得Java程序員可以更加專注于業務邏輯的實現,而不需要過多地關注底層硬件的細節。

? 版權聲明
THE END
喜歡就支持一下吧
點贊9 分享