20200407——java併發 volatile 九

CPU 高速緩存

線程是 CPU 調度的最小單元,線程涉及的目的最終仍然是更充分的利用計算機處理的效能,但是絕大部分的運算任務不能只依靠處理器“計算”就能完成,處理器還需要與內存交互,比如讀取運算數據、存儲運算結果,這個 I/O 操作是很難消除的。而由於計算機的存儲設備與處理器的運算速度差距非常大,所以現代計算機系統都會增加一層讀寫速度儘可能接近處理器運算速度的高速緩存來作爲內存和處理器之間的緩衝:將運算需要使用的數據複製到緩存中,讓運算能快速進行,當運算結束後再從緩存同步到內存之中。

在這裏插入圖片描述

緩存一致性協議

處理器上有一套完整的協議,來保證 Cache 的一致性,比較經典的應該就是MESI 協議了,它的方法是在 CPU 緩存中保存一個標記位,這個標記爲有四種狀態

Ø M(Modified) 修改緩存,當前 CPU 緩存已經被修改,表示已經和內存中的數據不一致了

Ø I(Invalid) 失效緩存,說明 CPU 的緩存已經不能使用了

Ø E(Exclusive) 獨佔緩存,當前 cpu 的緩存和內存中數據保持一直,而且其他處理器沒有緩存該數據

Ø S(Shared) 共享緩存,數據和內存中數據一致,並且該數據存在多個 cpu緩存中每個 Core 的 Cache
控制器不僅知道自己的讀寫操作,也監聽其它 Cache 的讀寫操作,嗅探(snooping)"協議

Java Memory Model 內存模型

內存模型定義了共享內存系統中多線程程序讀寫操作行爲的規範,來屏蔽各種硬件和操作系統的內存訪問差異,來實現 Java 程序在各個平臺下都能達到一致的內存訪問效果。Java 內存模型的主要目標是定義程序中各個變量的訪問規則,也就是在虛擬機中將變量存儲到內存以及從內存中取出變量(這裏的變量,指的是共享變量,也就是實例對象、靜態字段、數組對象等存儲在堆內存中的變量。而對於局部變量這類的,屬於線程私有,不會被共享)。通過這些規則來規範對內存的讀寫操作,從而保證指令執行的正確性。它與處理器有關、與緩存有關、與併發有關、與編譯器也有關。他解決了 CPU多級緩存、處理器優化、指令重排等導致的內存訪問問題,保證了併發場景下的可見性、原子性和有序性,。內存模型解決併發問題主要採用兩種方式:限制處理器優化和使用內存屏障。

Java 內存模型定義了線程和內存的交互方式,在 JMM 抽象模型中,分爲主內存、工作內存。主內存是所有線程共享的,工作內存是每個線程獨有的。線程對變量的所有操作(讀取、賦值)都必須在工作內存中進行,不能直接讀寫主內存中的變量。並且不同的線程之間無法訪問對方工作內存中的變量,線程間的變量值的傳遞都需要通過主內存來完成,他們三者的交互關係如下:
  在這裏插入圖片描述
 
volatile防止指令重排序

指令重排的目的是爲了最大化的提高CPU利用率以及性能,CPU的亂序執行優化在單核時代並不影響正確性,但是在多核時代的多線程能夠在不同的核心上實現真正的並行,一旦線程之間共享數據,就可能會出現一些不可預料的問題,指令重排序必須要遵循的原則是,不影響代碼執行的最終結果,編譯器和處理器不會改變存在數據依賴關係的兩個操作的執行順序,(這裏所說的數據依賴性僅僅是針對單個處理器中執行的指令和單個線程中執行的操作.)這個語義,實際上就是as-if-serial語義,不管怎麼重排序,單線程程序的執行結果不會改變,編譯器、處理器都必須遵守as-if-serial語義。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章