楠木軒

深入理解Java虛擬機:高效併發之Java內存模型

由 公羊易綠 發佈於 科技

本文由新手java開發原創,歡迎關注,帶你一起長知識!計算機系統併發執行
  1. 高速緩存(Cache):作為內存與處理器之間的緩衝;

  2. 將運算需要使用的數據複製到緩存中,讓運算能快速進行,當運算結束後在衝緩存同步回內存之中,這樣處理器就無須等待緩慢的內存讀寫了。

  3. 緩存一致性(Cache Conherence)

  4. 當多處理器的運算任務都涉及同一塊主內存區域時,需要各個處理器訪問緩存時都遵循一定協議,在讀寫時要根據協議來進行操作(MSI、MESI、MOSI、Synapse、Firefly、Dragon Protocol)。

  5. 為了使處理器內部的運算單元能儘量被充分利用,處理器可能會對輸入代碼進行亂序執行優化,處理器會在計算之後將亂序執行的結果重組,保證該結果與順序執行的結果是一直的,但並不保證程序中各個語句計算的先後順序與輸入代碼中的順序一直。

處理器、高速緩存、主內存間的交互關係

Java內存模型
  1. Java內存模型(Java Memory Model,JMM):來屏蔽各種硬件和操作系統的內存訪問差異,以實現讓Java程序在各種平台下都能達到一致的內存訪問效果。

線程、主內存、工作內存三者的交互關係

  1. 主內存:對應於Java堆中的對象實例數據部分。

  2. 工作內存:對應於虛擬機棧中的部分區域。

內存間交互操作主內存與工作內存之間具體的交互協議
  1. lock(鎖定):作用於主內的變量,它把一個變量標識為一條線程獨佔的狀態。

  2. unlock(解鎖):作用於主內存的變量,它把一個處於鎖定狀態的變量釋放出來,釋放後的變量才可以被其他線程鎖定。

  3. read(讀取):作用於主內存的變量,它把一個變量的值從主內存傳輸到線程的工作內存中,以便隨後的load動作使用。

  4. load(載入):作用於工作內存的變量,它把read操作從主內存中得到的變量值放入工作內存的變量副本中。

  5. use(使用):作用於工作內存的變量,它把工作內存中一個變量的值傳遞給執行引擎,每當虛擬機遇到一個需要使用變量的值的字節碼指令時將會執行這個操作。

  6. assign(賦值):作用於工作內存的變量,它把一個執行引擎接受到的值賦給工作內存的變量,每當虛擬機遇到一個給變量賦值的字節碼指令時執行這個操作。

  7. store(存儲):作用於工作內存的變量,它把工作內存中一個變量的值傳送到主內存中,以便隨後的write操作使用。

  8. write(寫入):作用於主內存的變量,它把store操作從工作內存中得到的變量的值放入主內存的變量中。

執行基本操作時滿足的規則:
  1. 不允許read和load、store和write操作之一單獨出現,即不允許一個變量從主內存讀取了但工作內存不接受,或者工作內存發起回寫了但主內存不接受的情況出現。

  2. 不允許一個線程丟棄它最近的assign操作,即變量在工作內存中改變了之後必須把該變化同步回主內存。

  3. 不允許一個線程沒有發生過任何assigin操作把數據從線程的工作內存同步回主內存中。

  4. 一個新的變量只能在主內存中"誕生",不允許在工作內存中直接使用一個未被初始化(load或assign)的變量,就是對一個變量實施use、store操作之前,必須先執行assigin和load操作。

  5. 一個變量在同一時刻只允許一條線程對其進行lock操作,但lock操作可以被同一線程重複執行多次,多次執行lock後,只有執行相同次數的unlock操作,變量才會被解鎖。

  6. 如果對一個變量事先沒有被lock操作鎖定,那就不允許對它執行unlock操作,也不允許去unlock一個被其他線程鎖定的變量。

  7. 對一個變量unlock操作之前,必須先把此變量同步回主內存中(執行store、write操作)。

volatile特性
  1. 保證此變量對所有線程的可見性,但對於運算不是原子操作(當一條線程修改了一個變量的值,新值對於其他線程來説是可以立即得知的)。

  • 由於volatile變量只能保證可見性,在不符合規則的運算場景中,仍然需要枷鎖來保證原子性

    • 運算結果不依賴變量的當前值,或者能夠保證只有單一的線程修改變量的值。

    • 變量不需要與其他的狀態變量共同參與不變約束。

  1. 禁止指令重排序優化。

原子性、可見性與有序性
  1. 原子性:基本數據類型的訪問、讀寫都是具備原子性的;大範圍的原子性保證(synchronized)

  2. 可見性:當一個線程修改了共享變量的值時,其他線程能夠立即得知這個修改。(synchronized、final)

  3. 有序性:volatile、synchronized兩個關鍵字保證線程之間的有序性;volatile禁止了指令重排序,synchronized則是一個變量在同一個時刻只允許一條線程對其進行了lock操作。

Happens-before原則
  1. 程序次序規則:在一個線程內,按照控制流順序,書寫在前面的操作先行發生於書寫在後面的操作。

  2. 管程鎖定規則:一個unlock操作先行生髮於後面對同一個鎖的lock操作。

  3. volatile變量規則:對一個volatile變量的寫操作先行發生於後面對這個變量的讀操作。

  4. 線程啓動規則:Thread對象的start()方法先行發生於此線程的每一個動作。

  5. 線程終止規則:線程中的所有操作都先行發生於對此線程的終止檢查,通過Thread::join()方法是否結束、Thread::isAlive()的返回值等檢測線程是否已經終止執行。

  6. 對象終結規則:一個對象的初始化完成先行發生於它的finalize()方法的開發。

  7. 傳遞性:如果操作A先行發生於操作B,操作B先行發生於操作C,那麼操作A先行發生於操作C。

之後會對volatile、final、synchronized等會重點分享他們相應的happens-before關係、共享變量間的狀態轉變及讀寫指令序列設計到的內存屏障問題。