Udemy Course Multithreading, Concurrency & Performance 19

Race Condition與Data Races

ZONGRU Li
Aug 31, 2021

Race Condition:

前面也有提到過

在Mult-thread的邏輯運算中,不同邏輯使用到了相同的resource

這兩個沒有限定為atomic的邏輯在做同一物件計算可能會造成非正確的結果

例如有個起始物件是int items=0

並且有兩個邏輯分別被兩個threads執行,邏輯分別是

一個是IncrementingThread在做items++

一個是DecrementingThread在做items--

當兩個Thread同時運行可能Step如下:

當前結果items=1

也有可能是如下:

其當前結果為items=-1

Data Races:

是另一種可能要成問題的狀況

考量以下狀況:

有一class內有兩個共用的recource物件x與y,並且初始值都是0

在有兩個Thread分別運算不同區塊的Method

由上述程式邏輯考慮,x的數值變化都比y前面

亦即x理當總是比y還大,所以Thread2內永遠部會拋出Exception文字才對

可能運算順序與結果如下:

也可能如下

因為在checkForDataRace內,y太早被讀取,而早期y的值還在0

若increment中間計算多次,也可能像下面這樣

也可能如下:

所以永久不變的結果就是:

x≥y

下面實際演練上述邏輯:

由上面推演x++一直在y++前面,理論上永遠不會印出print文字

並在Main裡面分別持續地呼叫上述的兩個方法

但是很可怕地結果卻是:

Oh My God! What Happened!

完整上述Code如下:

上述我們看到的現象就是:Data Race

造成此一現象的原因有如下:

過多的編譯CPU運算

為了更好的表現及硬體的使用效率,會有脫離運行順序的狀況

並且這兩者也是為了同時保持代碼的邏輯正確性

並且其中造成脫序也是為了將運算加速

其中編譯工程師將編譯設計會重新安排執行順序是為了:

  1. Branch Predication(分支預測)(最佳化if判斷式.etc)
  2. Vectorization(矢量化)(parallel instruction execution並行指令執行SIMD)
  3. Prefetching instruction(預取指令)(更佳的cache performance)

而CPU重新安排執行順序只是為了更有效率的硬體單位的使用

而如以下就不會有跳脫順序的問題:

因為每一行都依賴前一行結果,此運算邏輯永遠不存在Data Race

而如何避免造成前面Data Race現象,Java提供的解法前面也已學到過:

  1. Synchronized關鍵字加到Method上用在共用變數的處理
  2. 宣告變數的時候加上volatile關鍵字

volatile使用如下

應該是指CPU運行指令,不會在read/write那行甚至橫跨執行

所以前面那個Code的以下這部分:

簡單的調整為:

加上volatile鎖

再次執行!

console上就啥都看不到了

最終code變成以下:

結論:

  1. Synchronized關鍵字可以解決Race Condition & Data Race,但是有Performance Penalty(處罰)
  2. Volatile :

a. 解決Race Condition read/write from/to long & double

b. 解決Data Race是因為保證了順序

所以在被Thread運算過程改變數值的共用變數理應被Synchronized區塊包裹(或其他方式的lock)

或宣告為Volatile

參考課程

--

--

ZONGRU Li
ZONGRU Li

Written by ZONGRU Li

2022/11/17 開源部分個人筆記給LINE "Java程式語言討論區"社群,希望能對社群的技術學習做一點點貢獻.(掩面....記得退訂閱!

No responses yet