多執行緒去訪問同乙個集合,一般沒問題,執行緒安全問題一般是出在同時修改乙個物件的時候。
執行緒安全問題:一段**,單執行緒執行和多執行緒執行,結果不一致。
例如這個方法,很簡單,迴圈開啟task往乙個list裡add,等3秒鐘執行完後列印出來list的長度,如果是單執行緒,肯定是10000,現在看看多執行緒的結果。
而且基本上每次執行的結果都不一樣,這說明,出現了多執行緒安全的問題,因為當多個執行緒會出現同時對list的同乙個下標進行add的操作,導致值的覆蓋,也就是同時往記憶體的同乙個位址進行了多次賦值,導致先賦值進去的丟失。
最常用的解決方案是 lock
lock是monitor的語法糖,它其實是鎖住了乙個記憶體的引用位址,所以lock的物件肯定不能是值型別,因為值型別就儲存在棧中,根本就沒有向堆引用,那怎麼鎖,另外null也不能鎖。
那麼當多執行緒執行到lock這裡時,第乙個到達的執行緒會佔據這個lock_obj的引用,也就是加了個狀態,當別的執行緒企圖訪問這個**塊前,需要先看看這個lock_obj的狀態,發現被佔據了,就會等待至佔據的執行緒執行完**塊並重置這個狀態。至於會不會多個執行緒同時佔據引用,目前是不會,因為這是原子級別的操作,想一想事務的原子性的概念,不可分割,大概是這個意思。
宣告乙個object物件,用lock鎖住add這一段
///結果///鎖物件,官方標準推薦寫法。
/// private
static
readonly
object lock_obj = new
object
();
private
static
void
asyncsafemethod()
});}
thread.sleep(
3000
); console.writeline(listint.count);
}
這裡說一下為什麼是鎖變數推薦 private static readonly
私有是防止有其他**方法這個鎖變數,自己的鎖就自己用,否則有可能形成不同的功能之間的互相競爭,功能之間無法併發,形成阻塞。
靜態是確保不同的例項呼叫的是同乙個鎖變數,如果不是靜態,在一段**中例項化多次這個類的物件時,每次的鎖都是新的,這樣多次例項化的物件呼叫加鎖的方法,它們之間會形成併發。
唯讀,如果不是唯讀,有人把鎖變數重新改了,那之間的鎖就失效了,因為引用已經變了。
為啥不能用string當鎖變數呢?因為string型別比較特殊,多個string如果賦值相同的話,不會被視為不同的物件,而是通過同樣的引用指向同乙個堆的位址,這樣你用這多個string當鎖變數的話,互相之間會競爭,無法併發。
使用lock,其實就是保證被它包起來的**塊,同一時刻只能有乙個執行緒訪問,其他要排隊,說白了,就是單執行緒化。
多執行緒 執行緒安全
原因 當多個執行緒同時共享,同乙個全域性變數或靜態變數。做寫的操作時,可能發生資料衝突問題,也就是執行緒安全問題。但是做讀操作是不會發生資料衝突問題。解決方案 方式一 內建鎖synchronized synchronized保證執行緒原子性,當執行緒進入方法的時候,自動獲取鎖,一旦鎖被其它執行緒獲取...
多執行緒 執行緒安全
public class unsafethread t.start while thread.activecount 1 system.out.println sum 1 從主記憶體中講sum變數複製到執行緒的工作記憶體 2 在工作記憶體中修改變數 1操作 3 將sum變數從執行緒的工作記憶體寫回到...
多執行緒 執行緒安全
執行緒安全 多個執行流對臨界資源的爭搶訪問,但是不會出現資料二義性 執行緒安全的實現 同步 通過條件判斷保證對臨界資源訪問的合理性 互斥 通過同一時間對臨界資源訪問的唯一性實現臨界資源訪問的安全性 互斥鎖實現的原理 互斥鎖本身是乙個只有0 1的計數器,描述了乙個臨界資源當前的訪問狀態,所有執行流在訪...