在多執行緒內使用集合,如果未對集合做任何安全處理,就非常容易出現系統崩潰或各種錯誤。最近的專案裡,使用的是socket通訊後再改變了某個集合,結果導致系統直接崩潰,且無任何錯誤系統彈出。
經排查,發現問題是執行某集合後,系統就會在一定時間內退出,最後發現是使用的乙個字典集合出了問題。稍微思考後,就認定了是執行緒安全問題。因為此集合在其它幾個地方都有執行緒做迴圈讀取。
下面是我模擬的乙個示例,沒有進行任何的安全處理:
1在上面的示例中,建立了乙個dictionary字典對像,程式執行時,輸出了下面的錯誤:class
program214
public
static
void
addmethod()
1521}22
public
static
void
readmethod()
2333}34
}35}36
public
class
mycollection
3746
else
4750}51
52public
void remove(string
key)
5358
}59 }
程式執行時,輸出了上面的錯誤,僅僅輸出了一行結果
這次測試有明顯示的錯誤提示,集合已修改;可能無法執行列舉操作。
唉,真是乙個常見的問題,在foreach的時侯又修改集合,就一定會出現問題了,因為foreach是唯讀的,在進行遍歷時不可以對集合進行任何修改。
看到這裡,我們會想到,如果使用for迴圈進行逆向獲取,也許可以解決此問題。
非常可惜,字典對像沒有使用索引號獲取的辦法,下面的****(
type
內部結構
支援索引
記憶體占用
隨機插入的速度(毫秒)
順序插入的速度(毫秒)
根據鍵獲取元素的速度(毫秒)
未排序字典
dictionary
雜湊表否
2230
3020
hashtable
雜湊表否
3850
5030
listdictionary鍊錶否
3650000
50000
50000
ordereddictionary
雜湊表 +陣列是59
7070
40排序字典
sorteddictionary
紅黑樹否
20130
100120
sortedlist
2xarray是20
3300
3040
sortlist
2xarray是27
4500
100180
從時間複雜度來講,從字典中通過鍵獲取值所耗費的時間分別如下:
這可如何是好,只能改為可排序的對像?然後使用for解決?
我突然想到,是否可以在迴圈時縮短foreach,來解決此問題呢?
想到可以在迴圈時先copy乙份副本,然後再進行迴圈操作,編寫**,查詢copy的方法。真是無奈,沒有提供任何的copy方法。唉!看來人都是用來被逼的,先改個物件吧:
把dictionary修改成了hashtable對像(也沒有索引排序)。**如下:
1**一如即往的報錯,錯誤資訊一樣。class
program214
public
static
void
addmethod()
1521}22
public
static
void
readmethod()
2333}34
}35}36
public
class
mycollection
3747
else
4851}52
53public
void remove(string
key)
5459
}60 }
使用copy法試試
1輸出結果如下:class
program214
public
static
void
addmethod()
1521}22
public
static
void
readmethod()
2336}37
}38}39
public
class
mycollection
4050
else
5154}55
56public
void remove(string
key)
5762
}63 }
以上結果輸出
寫到這裡,我自己都有些模糊了。這文章和執行緒安全有毛關係。
根據msdn執行緒安全解釋如下:
hashtable
從頭到尾對乙個集合進行列舉本質上並不是乙個執行緒安全的過程。即使乙個集合已進行同步,其他執行緒仍可以修改該集合,這將導致列舉數引發異常。若要在列舉過程中保證執行緒安全,可以在整個列舉過程中鎖定集合,或者捕捉由於其他執行緒進行的更改而引發的異常。
經過我們模擬,沒有發現多執行緒下錯誤,但為安全起見,我們在使用時,最好根據msdn所述,在對執行緒操作時加上安全鎖處理,這裡我們不需自己定義鎖物件,因為微軟直接提供了syncroot進行安全鎖處理。
修改後的**如下:
時間損耗
1public
static
void
readmethod()
213 console.writeline("
\r\n******************************===\r\n");
14foreach (dictionaryentry item in
tempht)
1520
if (tempht != null && tempht.count == 20)21
24}25 stopwatch.stop(); //
停止監視
26 timespan timespan = stopwatch.elapsed; //
獲取當前例項測量得出的總時間
27 console.writeline("
全部加滿用時:
好了,多執行緒安全問題就說到這裡,總結來說就是注意鎖在多執行緒中的應用。
如有此文章內存在問題,還請多多指正。
Java多執行緒理解 執行緒安全的集合物件
1 概念介紹 2 執行緒安全的集合物件 3 測試 3 原因分析 4 執行緒安全的集合並不安全 分析以下場景 synchronized map return value 由於執行緒安全的集合物件是基於單個方法的同步,所以即使map是執行緒安全的,也會產生不同步現象。在非單個方法的場景下,我們仍然需要使...
(多執行緒)多執行緒的併發安全
多執行緒併發操作同乙個資源 同步鎖 多執行緒操作的鎖必須唯一 必須搞清楚 哪些 需要同步?那些在操作共享資源的 只要包含非讀的操作,或者根據共享資源進行條件判斷的,就需要同步!同步 塊解決 package com.gc.thread 多執行緒操作共享資源 併發 執行緒安全問題 同步 鎖 相對而言效能...
執行緒安全的集合
集合大概有4種型別 list set queue map其中vector hashtable properties是執行緒安全的。其中arraylist linkedlist hashset treeset hashmap treemap等都是執行緒不安全的。執行緒不安全是指 當多個執行緒訪問同乙個...