1. 程式人生 > >java高併發(三)併發程式設計的基礎

java高併發(三)併發程式設計的基礎

CPU多級快取

為什麼需要CPU快取?

原因是,CPU的頻率太快了,快到主存跟不上,這樣在處理器時鐘週期內,CPU常常需要等待主存,浪費資源。所以cache的出現,是為了緩解CPU和記憶體之間速度的不匹配問題。

CPU快取有什麼意義?

  • 時間區域性性:如果某個資料被訪問,那麼在不久的將來它很有可能被再次訪問。
  • 空間區域性性:如果某個資料被訪問,那麼與它相鄰的資料很快也可能被訪問。

CPU多級快取-快取一致性(MESI)

用於保證多個CPU cache之間快取共享資料的一致

CPU多級快取-亂序執行優化

處理器為了提高執行速度而做出的違背程式碼原有順序的優化。在單核情況下沒有問題,但是在多核環境下會出現問題。

JAVA記憶體模型(Java Memory Model,JMM)

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

如上圖所示,java記憶體模型要求,呼叫棧和本地變數存放線上程棧上,如圖中Thread Stack,物件存放在堆中(Heap)。本地變數Local variable2是一個指向物件的引用,Local variable存放線上程棧上,但物件存放在堆上。存放在堆上的物件可以被所持有對這個物件引用的執行緒訪問,並且可以訪問這個物件的成員變數,而當兩個執行緒同時訪問object3上的同一個方法時,都會訪問方法中的成員變數,那麼每一個執行緒會擁有這個物件成員變數的私有拷貝。

執行緒之間的通訊必須經過主記憶體,例如執行緒A想要與執行緒B進行通訊,執行緒A需要將共享變數的副本重新整理到主記憶體中,然後執行緒B讀取主記憶體的執行緒A更新過的共享變數。因此會出現同步問題。

同步往往有8種操作:

  • lock(鎖定):作用於主記憶體的變數,把一個變數標識為一條執行緒獨佔狀態。
  • unlock(解鎖):作用於主記憶體的變數,把一個處於鎖定狀態的變數釋放出來,釋放後的變數才可以被其他執行緒鎖定。
  • read(讀取):作用於主記憶體的變數,把一個變數值從主記憶體傳輸到執行緒的工作記憶體中,以便隨後的load動作使用
  • load(載入):作用於工作記憶體的變數,它把read操作從主記憶體中得到的變數放入工作記憶體的變數副本中。
  • use(使用):作用於工作記憶體的變數,把工作記憶體中的一個變數值傳遞給執行引擎。
  • assign(賦值):作用於工作記憶體的變數,它把一個執行引擎接收到的值賦值給工作記憶體的變數。
  • store(儲存):作用於工作記憶體的變數,把工作記憶體中的一個變數的值傳送到主記憶體中,以便隨後的write操作。
  • write(寫入):作用於主記憶體的變數,它把store操作從工作記憶體中一個變數的值傳送到主記憶體的變數中。

同步規則:

  • 如果要把一個變數從主記憶體中複製到工作記憶體,就需要按順序的執行read和load操作;如果把變數從工作記憶體中同步回主記憶體中,就要按順序地執行store和write操作。但java記憶體模型只要求上述操作必須按順序執行,而沒有保證必須是連續執行。
  • 不允許read和load、store和write操作之一單獨出現。
  • 不允許一個執行緒丟棄它的最近assign的操作,即變數在工作記憶體中改變了之後必須同步到主記憶體中。
  • 不允許一個執行緒無原因的(沒有發生過任何assign操作)把資料從工作記憶體同步回主記憶體中。
  • 一個新的變數只能在主記憶體中誕生,不允許在工作記憶體中直接使用一個未被初始化(load或assign)的變數。即就是對一個變數實施use和store操作之前,必須先執行過assign和load操作。
  • 一個變數在同一時刻只允許一條執行緒對其進行lock操作,但lock操作可以被同一條執行緒重複執行多次,多次執行lock後,只有執行相同次數的unlock操作,變數才會被解鎖。lock和unlock必須成對出現。
  • 如果對一個變數執行lock操作,將會清空工作記憶體中此變數的值,在執行引擎使用這個變數前需要重新執行load或assign操作初始化變數的值。
  • 如果一個變數事先沒有被lock操作鎖定,則不允許對它執行unlock操作;也不允許去unlock一個被其他執行緒鎖定的變數。
  • 對一個變數執行unlock操作之前,必須先把此變數同步到主記憶體中(執行store和write操作)

 

併發的優勢與風險

優勢:

  • 速度:同時處理多個請求,響應更快;複雜的操作可以分成多個程序同時進行。
  • 設計:程式設計在某些情況下更簡單,也可以有更多的選擇。
  • 資源利用:CPU能夠在等待IO的時候做一些其他的事情。

風險:

  • 安全性:多個執行緒共享資料時可能會產生與期望不相符的結果
  • 活躍性:某個操作無法繼續進行下去時,就會發生活躍性問題。比如死鎖、飢餓等問題。
  • 效能:執行緒過多會使得CPU頻繁切換,排程時間增多,同步機制,消耗過多記憶體。