1. 程式人生 > 其它 >【9033期】JUC多執行緒---JMM記憶體模型與volatile記憶體語義

【9033期】JUC多執行緒---JMM記憶體模型與volatile記憶體語義

JMM 記憶體模型

什麼是 JMM 記憶體模型

Java 記憶體模型是 Java 虛擬機器定義的一種多執行緒訪問 Java 記憶體各個變數的訪問規範,主要圍繞如何解決併發過程中的原子性、可見性、有序性這三個問題來解決執行緒的安全問題。

Java 記憶體模型將記憶體分為了主記憶體和工作記憶體(也稱為棧空間)。主記憶體存放所有的共享變數,所有執行緒都可以訪問。每個執行緒都有自己的工作記憶體,儲存了該執行緒使用到的變數的副本,執行緒對變數的所有操作都必須在自己的工作記憶體中完成,不能直接操作主存中的變數。操作時,首先將變數從主記憶體拷貝到自己的工作記憶體中,然後在自己的工作記憶體中對變數進行操作,操作完成後再將變數寫回主存。不同的執行緒間也無法直接訪問對方的工作記憶體的變數,執行緒間的變數值的傳遞必須通過主記憶體來完成。

(1)原子性:原子性指的是一個操作是不可中斷的,即使是在多執行緒環境下,一個操作一旦開始就不會被其他執行緒影響。

(2)可見性:可見性指的是,當一個執行緒修改了某個共享變數的值,其他執行緒能夠馬上得知這個修改的值。

序列程式不存在可見性問題,因為在任何一個操作中修改了某個變數的值,後續的操作中都能讀取這個修改過的變數值。但在多執行緒環境中可就不一定了,因為執行緒對共享變數的操作都是拷貝到各自的工作記憶體中進行操作後才寫回到主記憶體中的,這就可能存在一個執行緒A修改了共享變數x的值,還未寫回主記憶體時,另外一個執行緒B又對主記憶體中同一個共享變數x進行操作,但此時A執行緒工作記憶體中共享變數x對執行緒B來說並不可見,這種工作記憶體與主記憶體同步延遲現象就造成了可見性問題,另外指令重排以及編譯器優化也可能導致可見性問題。

(3)有序性:對於多執行緒環境,因為程式編譯成機器碼指令後可能會出現指令重排現象,重排後的指令與原指令的順序未必一致,有可能出現亂序現象。

指令重排序:計算機在執行程式時,為了提高效能,編譯器和處理器的常常會對指令做重排,在單執行緒條件下,指令重排序可以保證執行結果的一致性,但是在多執行緒條件下,這些重排優化可能會導致程式出現記憶體可見性問題,不能保證多執行緒間語義一致性。

原子性、可見性、有序性問題的解決措施

(1)原子性問題:除了 JVM 自身提供對基本資料型別讀寫操作的原子性外,對於方法級別或者程式碼級別的原子性操作,可以使用 synchronized 關鍵字或者重入鎖 ReentrantLock 保證程式執行的原子性。

(2)可見性問題:工作記憶體與主記憶體同步延遲現象導致的可見性問題,可以使用 synchronized 關鍵字、Lock 或者 volatile 關鍵字解決,它們都可以使一個執行緒修改後的變數立即對其他執行緒可見。

(3)有序性問題:可以利用 volatile、synchronized 關鍵字解決。

JMM 的 as-if-serial 規則和 happens-before 規則