Udemy Course Multithreading, Concurrency & Performance 19
Race Condition:
前面也有提到過
在Mult-thread的邏輯運算中,不同邏輯使用到了相同的resource
這兩個沒有限定為atomic的邏輯在做同一物件計算可能會造成非正確的結果
例如有個起始物件是int items=0
並且有兩個邏輯分別被兩個threads執行,邏輯分別是
一個是IncrementingThread在做items++
一個是DecrementingThread在做items--
當兩個Thread同時運行可能Step如下:
也有可能是如下:
Data Races:
是另一種可能要成問題的狀況
考量以下狀況:
有一class內有兩個共用的recource物件x與y,並且初始值都是0
在有兩個Thread分別運算不同區塊的Method
由上述程式邏輯考慮,x的數值變化都比y前面
亦即x理當總是比y還大,所以Thread2內永遠部會拋出Exception文字才對
可能運算順序與結果如下:
也可能如下
若increment中間計算多次,也可能像下面這樣
也可能如下:
所以永久不變的結果就是:
x≥y
下面實際演練上述邏輯:
並在Main裡面分別持續地呼叫上述的兩個方法
但是很可怕地結果卻是:
完整上述Code如下:
上述我們看到的現象就是:Data Race
造成此一現象的原因有如下:
過多的編譯與CPU運算
為了更好的表現及硬體的使用效率,會有脫離運行順序的狀況
並且這兩者也是為了同時保持代碼的邏輯正確性
並且其中造成脫序也是為了將運算加速
其中編譯工程師將編譯設計會重新安排執行順序是為了:
- Branch Predication(分支預測)(最佳化if判斷式.etc)
- Vectorization(矢量化)(parallel instruction execution並行指令執行SIMD)
- Prefetching instruction(預取指令)(更佳的cache performance)
而CPU重新安排執行順序只是為了更有效率的硬體單位的使用
而如以下就不會有跳脫順序的問題:
而如何避免造成前面Data Race現象,Java提供的解法前面也已學到過:
- Synchronized關鍵字加到Method上用在共用變數的處理
- 宣告變數的時候加上volatile關鍵字
volatile使用如下
所以前面那個Code的以下這部分:
簡單的調整為:
再次執行!
最終code變成以下:
結論:
- Synchronized關鍵字可以解決Race Condition & Data Race,但是有Performance Penalty(處罰)
- Volatile :
a. 解決Race Condition read/write from/to long & double
b. 解決Data Race是因為保證了順序
所以在被Thread運算過程改變數值的共用變數理應被Synchronized區塊包裹(或其他方式的lock)
或宣告為Volatile