2013-09-29 130 views
0

下面的代碼有什麼問題?下面的代碼不是線程安全的嗎?

private Map<Integer, Integer> aMap = new ConcurrentHashMap<Integer, Integer>();  
Record rec = records.get(id); 
    if (rec == null) { 
     rec = new Record(id); 
     records.put(id, rec); 
    } 
    return rec; 
  1. 是上面的代碼不線程安全?爲什麼在這種情況下我應該使用putIfAbsent
  2. 鎖定僅適用於更新。在檢索的情況下,其 允許完全併發。這個說法是什麼意思?

回答

6

這不是線程安全的。

  1. 如果有另一個線程,然後在records.getrecords.put之間的時間另一個線程可能已經把記錄爲好。

  2. 只讀操作(即不修改結構的操作)可以由多個線程同時完成。例如,1000個線程可以安全地讀取int的值。但是,如果沒有某種鎖定操作,這1000個線程無法更新int的值。

我知道,這聽起來像一個非常不太可能發生,但是請記住,在一百萬1事件爲1GHz發生每秒1000次。


這是線程安全的:

private Map<Integer, Integer> aMap = new ConcurrentHashMap<Integer, Integer>(); 
// presumably aMap is a member and the code below is in a function 
aMap.putIfAbsent(id, new Record(id)) 
Record rec = records.get(id); 
return rec; 

注意,這可能會創建一個Record而從不使用它。

+0

只讀操作(即不修改結構的操作)可以由多個線程同時完成。 - 這是否意味着完全併發? –

+0

我以前沒有聽說過這個詞,但在我看來,是的。 – Adam

+0

用putIfAbsent()替換put的最後一個問題是否使線程安全? –

3

它可能或不可能是線程安全的,這取決於你希望如何行動。

在代碼結束時,aMap將安全地擁有Recordid。但是,有可能兩個線程都會在中創建並放入一個Record,這樣有兩個(或更多,如果更多的線程這樣做)Records存在。這可能是好的,也可能不是 - 真的取決於你的應用程序。

線程安全的危險之一(例如,如果您使用正常的HashMap而沒有同步)是線程可以跨線程讀取部分創建或部分更新的對象;換句話說,事情可以去真的干擾。這將在您的代碼中發生而不是,因爲ConcurrentHashMap將確保內存在線程之間保持最新,並且從這個意義上講,它是線程安全的。

一兩件事你可以做的是使用putIfAbsent,這將原子放在一個鍵值對到地圖,但前提是沒有什麼那個鍵已經:

if (rec == null) { 
    records.putIfAbsent(id, new Record(id)); 
    rec = records.get(id); 
} 

在這種方法中,你可能會創建另一個Record對象,但如果是這樣,它將不會被插入,並立即可用於垃圾回收。由段的末尾:

  • records將包含給定id
  • 只有一個Record將曾經被投入records該ID的Record(無論是通過這個線程放在那裏或其他)
  • rec將指向該記錄
+0

我認爲你的意思是'records.putIfAbsent(..'而不是'records.put(..'在你的代碼片段 – Adam

+0

哎呀,是的,謝謝你。 – yshavit

+1

謝謝@yshavit你的回答...謝謝你的時間。 –

相關問題