ConcurrentMap.putIfAbsent(key,value)

Map<String, Locale> map = new HashMap<String,Locale>();  java

在單線程環境下能夠知足要求,可是在多線程環境下會存在線程安全性問題,即不能保證在併發的狀況相同的 key 返回同一個 Local 對象引用。安全

這是由於在上面的代碼裏存在一個習慣被稱爲 put-if-absent 的操做 [1],而這個操做存在一個 race condition:多線程

  1. if (locale == null) {  
  2.     locale = new Locale(language, country, variant);  
  3.     map.put(key, locale);  
  4. }  

 由於在某個線程作完 locale == null 的判斷到真正向 map 裏面 put 值這段時間,其餘線程可能已經往 map 作了 put 操做,這樣再作 put 操做時,同一個 key 對應的 locale 對象被覆蓋掉,最終 getInstance 方法返回的同一個 key 的 locale 引用就會出現不一致的情形。因此對 Map 的 put-if-absent 操做是不安全的(thread safty)。併發

爲了解決這個問題,java 5.0 引入了 ConcurrentMap 接口,在這個接口裏面 put-if-absent 操做以原子性方法 putIfAbsent(K key, V value) 的形式存在。app

  1.      *   if (!map.containsKey(key)) 
  2.      *       return map.put(key, value); 
  3.      *   else 
  4.      *       return map.get(key);</pre>

因此能夠使用該方法替代上面代碼裏的操做。可是,替代的時候很容易犯一個錯誤。請看下面的代碼:spa

  1. public class Locale implements Cloneable, Serializable {  
  2.     private final static ConcurrentMap<String, Locale> map = new ConcurrentHashMap<String, Locale>();  
  3.     public static Locale getInstance(String language, String country,  
  4.             String variant) {  
  5.         //...  
  6.         String key = some_string;  
  7.         Locale locale = map.get(key);  
  8.         if (locale == null) {  
  9.             locale = new Locale(language, country, variant);  
  10.             map.putIfAbsent(key, locale);  
  11.         }  
  12.         return locale;  
  13.     }  
  14.     // ...
  15. }  

 這段代碼使用了 Map 的 concurrent 形式(ConcurrentMap、ConcurrentHashMap),並簡單的使用了語句map.putIfAbsent(key, locale) 。這一樣不能保證相同的 key 返回同一個 Locale 對象引用。線程

這裏的錯誤出在忽視了 putIfAbsent 方法是有返回值的,而且返回值很重要對象

「若是(調用該方法時)key-value 已經存在,則返回那個 value 值。若是調用時 map 裏沒有找到 key 的 mapping,返回一個 null 值」接口

因此,使用 putIfAbsent 方法時切記要對返回值進行判斷,增長了對方法返回值的判斷:get

  1. Locale l = cache.putIfAbsent(key, locale);    
  2. if (l != null) {    
  3.     locale = l;    
  4. }    

這樣能夠保證併發狀況下代碼行爲的準確性。

相關文章
相關標籤/搜索