1. 程式人生 > 其它 >併發程式設計基礎底層原理學習(三)

併發程式設計基礎底層原理學習(三)

執行緒通訊與執行緒同步

​ 在併發程式設計中需要解決兩個關鍵問題:1.執行緒之間如何通訊 2.執行緒之間如何同步。執行緒通訊是指執行緒之間以何種機制來交換訊息。執行緒之間通訊機制有兩種:共享記憶體和訊息傳遞。在共享記憶體的併發模型裡,執行緒之間共享程式的公共狀態,通過寫-讀記憶體中的公共狀態進行通訊。而在訊息傳遞的併發模型裡,執行緒之間沒有公共狀態,執行緒之間必須通過傳送訊息進行通訊。

​ 執行緒同步是指程式中用於控制不同執行緒間操作發生的相對順序的機制。在共享記憶體併發模型裡,程式設計師必須顯式指定某個方法或某段嗲嗎需要線上程之間互斥執行。而在訊息傳遞的併發模型裡,由於訊息的傳送必須在訊息的接收之前,因此同步是隱式進行的。

Java記憶體模型--堆和棧

​ JVM內部使用的Java記憶體模型將記憶體劃分為棧和堆,如下圖所示。

Java 虛擬機器中執行的每個執行緒都有自己的執行緒棧。執行緒棧包含有關執行緒呼叫了哪些方法以到達當前執行點的資訊。在Java中所有例項域,靜態域和陣列元素都儲存在堆記憶體中,堆記憶體線上程之間共享。區域性變數,方法定義引數和異常處理器引數不會線上程之間共享,它們不會出現執行緒安全問題。執行緒堆疊還包含正在執行的每個方法的所有區域性變數(呼叫堆疊上的所有方法)。一個執行緒只能訪問它自己的執行緒堆疊。一個執行緒建立的區域性變數對於建立它的執行緒之外的所有其他執行緒都是不可見的。即使兩個執行緒正在執行完全相同的程式碼,這兩個執行緒仍將在各自的執行緒堆疊中建立該程式碼的區域性變數。因此,每個執行緒都有自己的每個區域性變數版本。所有原始型別的區域性變數(boolean、byte、short、char、int、long、float、double)都完全儲存線上程堆疊中,因此對其他執行緒不可見。

Java記憶體模型抽象結構

​ Java執行緒之間的通訊由Java記憶體模型(簡稱JMM)控制,JMM決定了一個執行緒對共享變數的寫入何時對另一個執行緒可見。JMM定義了執行緒和主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體中,每個執行緒都有一個私有的本地記憶體,本地記憶體中儲存了該執行緒以讀/寫共享變數的副本。本地記憶體是JMM的一個抽象概念,並不真實存在。

從上圖來看,執行緒 A 與執行緒 B 之間如要通訊的話,必須要經歷下面 2 個步驟:

  • 首先,執行緒 A 把本地記憶體 A 中更新過的共享變數重新整理到主記憶體中去。
  • 然後,執行緒 B 到主記憶體中去讀取執行緒 A 之前已更新過的共享變數。

如上圖所示,本地記憶體 A 和 B 有主記憶體中共享變數 x 的副本。假設初始時,這三個記憶體中的 x 值都為 0。執行緒 A 在執行時,把更新後的 x 值(假設值為 1)臨時存放在自己的本地記憶體 A 中。當執行緒 A 和執行緒 B 需要通訊時,執行緒 A 首先會把自己本地記憶體中修改後的 x 值重新整理到主記憶體中,此時主記憶體中的 x 值變為了 1。隨後,執行緒 B 到主記憶體中去讀取執行緒 A 更新後的 x 值,此時執行緒 B 的本地記憶體的 x 值也變為了 1。

從整體來看,這兩個步驟實質上是執行緒 A 在向執行緒 B 傳送訊息,而且這個通訊過程必須要經過主記憶體。JMM 通過控制主記憶體與每個執行緒的本地記憶體之間的互動,來為 java 程式設計師提供記憶體可見性保證。