1. 程式人生 > >併發與高併發

併發與高併發

一.併發程式設計基礎

1.1CPU多級快取

        (1)cpu快取

cpu從快取中取資料,快取中取不到通過主線去主存。快取的出現大大加快了cpu的讀寫效率。隨著發展出現了多級快取(一級,二級,三級等)。快取的出現主要是為了解決cpu運算速度與記憶體讀寫速度不匹配的問題。

(2)cpu快取的意義

時間區域性性:如果一個數據被訪問,那麼在不久的將來他很可能再被訪問。

空間區域性性:如果一個數據被訪問,那麼它相關的資料也可能被訪問。

(3)CPU多級快取

MESI(Modified,Exclusive, Shared, Invalidate):用於保證多個CPUcache之間快取共享資料的一致。

Cpu快取的資料被修改(modified)與主存的資料不一致。該快取行的內容在某個時間點寫回到主存,快取行的資料修改為獨享(exclusive)。其他cpu讀取該快取行的時候變成共享狀態。一個cpu修改共享快取行的時候,其他cpu的快取是可以被作廢的(invalidate)。

在多核系統中,每個核都有自己的快取來共享主存主線。每個cpu發出讀寫請求,快取的目的就是為了減少讀寫主存的次數。

(4)cpu亂序執行優化

處理器為提高運算速度而打亂程式碼原來順序但原則上不影響執行結果的優化。一個核對資料進行運算,運算完打個標記已完成,另外一個核根據標記位判斷所需要的資料是否準備就緒。這樣先打標記但實際運算並未完成(沒有計算完成,沒從處理器重新整理到主存),最終導致其他核使用錯誤的資料。

1.2JAVA記憶體模型(java memory model, JMM)

        Java記憶體模型是一種規範, 規範了java虛擬機器與計算機記憶體是如何工作的,規定了一個執行緒如何和何時可以看到其他執行緒修改過的共享變數的值,以及在必須時如何同步的訪問共享變數。

        Useruser = new User();

        我們知道堆是實際存放資料的地方即new物件的時候(執行時)動態分配一塊儲存空間,同時把儲存空間的初始地址放入棧中。堆記憶體由gc垃圾回收機制回收釋放的記憶體空間。棧資料共享,速度快(相對於堆)。棧中的資料大小與生存時間是確定的。Java記憶體模型要求呼叫棧和本地變數存放線上程棧上,實際物件本身存放在堆上。存在堆上的物件可以被持有物件引用的執行緒訪問。當兩個或多個執行緒同時訪問堆中同一物件的統一方法時,先copy一份 出來,擁有物件的私有拷貝。

        每個cpu內部包含一些列暫存器,cpu訪問暫存器的速度遠大於記憶體的速度。Cpu運算時往往從快取中取資料,運算結束後把快取資料重新整理到主存中。Cpu運算時從主存中取資料到快取記憶體中,甚至有時讀到暫存器中。當cpu需要將結果會寫到主存的時候,他需要將cpu的值重新整理到快取中,在某個時間點重新整理到主存中。

        每一個執行緒都有一份本地記憶體。這個本地記憶體是個抽象概念,包括了快取,寫緩衝區,暫存器,其他硬體以及編譯器的優化。本地記憶體儲存了執行緒對於主存讀寫資料的拷貝副本,使用它的目的是為了提高運算速度,虛擬機器以及硬體系統可能會讓工作記憶體優先儲存在高速記憶體以及暫存器中,java執行緒工作記憶體是cpu暫存器,快取記憶體的抽象描述。Java記憶體模型是對記憶體的物理劃分,而且只侷限在jvm記憶體。

        執行緒的通訊若必須經過主記憶體,那麼必須經過下面兩個步驟。執行緒A把本地記憶體的共享變數重新整理到主記憶體上,執行緒B到主執行緒讀取執行緒A重新整理的共享變數。

        分析多個執行緒訪問同一物件的同一方法(對變數進行加1操作)。每個執行緒對於同一物件資料有一份本地記憶體拷貝,不同執行緒之間是沒有相互通訊的。這樣A執行緒拿到變數的同時,B執行緒也可能從主存的拿到相同的變數值,計算完之後重新整理到主存,導致結果非邏輯設計的平行計算。這時就需要我們增加一些同步的手段,來保證併發時存續處理的準確性。

1.3JAVA記憶體模型-同步八中操作

(1)lock(鎖):作用於主記憶體的變數,把一個變數標識為一條執行緒獨佔狀態。

(2)unlock(解鎖):作用於主記憶體變數,把一個處於鎖狀態的變數釋放出來,釋放後的變數能被其他執行緒鎖定。

(3)read(讀取):作用於主記憶體變數,把一個變數值從主記憶體傳輸到執行緒的工作記憶體。以方便後續高效load操作。

(4)load(載入):作用於工作記憶體的變數,它把read操作從主存中得到的變數放入到工作記憶體的變數副本中。

(5)use(使用):作用於工作記憶體的變數,把工作記憶體的變數值傳遞給執行引擎。

(6)assign(賦值):作用於工作記憶體的變數,它把一個從執行引擎收到的值賦值給工作記憶體的變數。

(7)store(儲存):作用於工作記憶體的變數,把工作記憶體中的一個變數值傳送到主記憶體中,以便隨後的write的操作。

(8)write(寫入):作用於主記憶體的變數,它把store操作從工作記憶體中的變數傳送到主記憶體的變數中。

1.4同步規則

(1)若把一個變數從記憶體賦值到工作記憶體,那麼就需要按順序執行read,load操作。若把變數從工作記憶體同步回主存,也需要按順序執行store和write操作。但java記憶體模型只要求上述操作必須按照順序執行,而沒有保證必須時連續執行。

(2)不允許read和load,store和write操作單一出現。

(3)不允許一個執行緒丟棄它最近的assign操作,即變數在工作記憶體中改變以後必須同步到主記憶體中。

(4)不允許一個執行緒毫無原因(無assign操作)把資料從工作記憶體同步回主存中。

(5)一個新的變數只能在主記憶體中誕生,不允許在工作記憶體中使用一個未被初始化(load,assign)的變數。即對一個變數執行user,store操作之前,必須先執行assign和load操作。

(6)一個變數同一時刻只允許一個執行緒對其進行lock操作,但是lock之後可以被同一條執行緒重複執行多次,多次執行lock後,需要執行相同次數的unlock操作,變數才能釋放鎖。Lock和unlock必須成對出現。

(7)若一個變數執行lock操作,將會清空工作記憶體中此變數的值,在執行引擎使用這個變數前需要重新load或assign操作初始化變數的值。