Java做爲一個跨平臺的語言。它的實現要面對不一樣的底層硬件系統,設計一箇中間層模型來屏蔽底層的硬件差別,給上層的開發人員一個一致的使用接口。Java內存模型就是這樣一箇中間層的模型,它爲程序猿屏蔽了底層的硬件實現細節,支持大部分的主流硬件平臺。編程
要理解Java內存模型以及一些處理高併發的技術手段,理解一些主要的硬件知識是必須的。緩存
這篇會說一下跟併發編程相關的一些硬件知識。網絡
一個主要的CPU運行計算的步驟例如如下:併發
1. 程序以及數據被載入到主內存高併發
2. 指令和數據被載入到CPU的快速緩存性能
3. CPU運行指令,把結果寫到快速緩存優化
4. 快速緩存中的數據寫回主內存.net
這個過程當中,咱們可以看到有兩個問題線程
1. 現代的計算芯片都會集成一個L1快速緩存,咱們可以理解爲每個芯片都有一個私有的存儲空間。設計
那麼當CPU的不一樣計算芯片要訪問同一個內存地址時,該內存地址的值會在CPU的不一樣計算芯片之間有多個拷貝,怎樣同步這些拷貝?
2. CPU讀寫是直接和快速緩存打交道。而不是和主內存直接打交道。因爲一般一次主存訪問在幾十到幾百個時鐘週期。而一次L1快速緩存的讀寫僅僅需要1-2個時鐘週期,而一次L2快速緩存的讀寫僅僅需要數十個時鐘週期。
那麼CPU寫到快速緩存的值什麼時候寫回到主內?假設是多個計算芯片在處理同一個內存地址。那麼怎樣處理這個時間差是個問題。
對於第一個問題。不一樣的硬件結構處理的方式不同。咱們來理解一下互連線的概念。
互連線是處理器於主存以及處理器與處理器之間進行通訊的媒介,有兩種主要的互聯結構:SMP(symmetric multiprocessing 對稱多處理)和NUMA(nonuniform memory access 非一致內存訪問)
SMP系統結構很是普通。因爲它們最easy構建。很是多小型server採用這樣的結構。處理器和存儲器之間採用總線互聯。處理器和存儲器都有負責發送和監聽總線廣播的信息的總線控制單元。但是同一時刻僅僅能有一個處理器(或存儲控制器)在總線上廣播,所有的處理器都可以監聽。
很是easy看出,對總線的使用是SMP結構的瓶頸。
NUMP系統結構中,一系列節點經過點對點網絡互聯,像一個小型互聯網,每個節點包括一個或多個處理器和一個本地存儲器。一個節點的本地存儲對於其它節點是可見的,所有節點的本地存儲一塊兒造成了一個可以被所有處理器共享的全局存儲器。可以看出,NUMP的本地存儲是共享的,而不是私有的,這點和SMP是不一樣的。NUMP的問題是網絡比總線複製。需要更加複雜的協議。處理器訪問本身節點的存儲器速度快於訪問其它節點的存儲器。NUMP的擴展性很是好,因此眼下很是多大中型的server在採用NUMP結構。
對於上層程序猿來講。最需要理解的是互連線是一種重要的資源。使用的好壞會直接影響程序的運行性能。
大概理解了不一樣的互連結構以後,咱們來看看緩存一致性協議。它主要就是處理多個處理器處理同一個主存地址的問題。
MESI是一種主流的緩存一致性協議,已經用在Pentium和PowerPC處理器中。它定義了緩存塊的幾種狀態
上圖展現了MESI快速緩存一致性協議的狀態轉換實例。
1. 在a中,處理器A從地址a讀取數據,將數據存入它的緩存並置爲exclusive
2. 在b中。當處理器B試圖從一樣地址a讀取數據時。A檢測到地址衝突,以相關數據作出響應。此時a同一時候被A和B以shared狀態裝入緩存
3. 在c中,當B要對共享地址a進行寫操做。則將狀態改成modified,並廣播提醒A,讓它將它的緩存塊狀態設置爲Invalid
4. 在d中。當A試圖從a讀取數據,會廣播它的請求。B則把它改動的數據發送到A和主存,並設置兩個副本的狀態爲shared來作出響應
不少其它緩存一致性協議的細節參考這篇 http://blog.csdn.net/realxie/article/details/7317630
緩存一致性協議存在的一個最大的問題是可能引發緩存一致性流量風暴。以前咱們看到總線在同一時刻僅僅能被一個處理器使用。當有大量緩存被改動,或者同一個緩存塊一直被改動時,會產生大量的緩存一致性流量。從而佔用總線。影響了其它正常的讀寫請求。
一個最多見的樣例就是假設多個線程對同一個變量一直使用CAS操做。那麼會有大量改動操做。從而產生大量的緩存一致性流量,因爲每一次CAS操做都會發出廣播通知其它處理器,從而影響程序的性能。
後面咱們會講怎樣優化這樣的使用方式。
對於第二個問題,怎樣處理改動數據從快速緩存到主內存的時間差。一般使用內存屏障來處理,後面會有專門的主題。
轉載請註明來源:http://blog.csdn.net/iter_zc