xylz,imxylz

          關注后端架構、中間件、分布式和并發編程

             :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            111 隨筆 :: 10 文章 :: 2680 評論 :: 0 Trackbacks

          從這一節開始正式進入并發容器的部分,來看看JDK 6帶來了哪些并發容器。

          在JDK 1.4以下只有Vector和Hashtable是線程安全的集合(也稱并發容器,Collections.synchronized*系列也可以看作是線程安全的實現)。從JDK 5開始增加了線程安全的Map接口ConcurrentMap和線程安全的隊列BlockingQueue(盡管Queue也是同時期引入的新的集合,但是規范并沒有規定一定是線程安全的,事實上一些實現也不是線程安全的,比如PriorityQueue、ArrayDeque、LinkedList等,在Queue章節中會具體討論這些隊列的結構圖和實現)。

           

          在介紹ConcurrencyMap之前先來回顧下Map的體系結構。下圖描述了Map的體系結構,其中藍色字體的是JDK 5以后新增的并發容器。

          image

          針對上圖有以下幾點說明:

          1. Hashtable是JDK 5之前Map唯一線程安全的內置實現(Collections.synchronizedMap不算)。特別說明的是Hashtable的t是小寫的(不知道為啥),Hashtable繼承的是Dictionary(Hashtable是其唯一公開的子類),并不繼承AbstractMap或者HashMap。盡管Hashtable和HashMap的結構非常類似,但是他們之間并沒有多大聯系。
          2. ConcurrentHashMap是HashMap的線程安全版本,ConcurrentSkipListMap是TreeMap的線程安全版本。
          3. 最終可用的線程安全版本Map實現是ConcurrentHashMap/ConcurrentSkipListMap/Hashtable/Properties四個,但是Hashtable是過時的類庫,因此如果可以的應該盡可能的使用ConcurrentHashMap和ConcurrentSkipListMap。

           

          回到正題來,這個小節主要介紹ConcurrentHashMap的API以及應用,下一節才開始將原理和分析。

          ConcurrentMap API

          除了實現Map接口里面對象的方法外,ConcurrentHashMap還實現了ConcurrentMap里面的四個方法。

           

          V putIfAbsent(K key,V value)

          如果不存在key對應的值,則將value以key加入Map,否則返回key對應的舊值。這個等價于清單1 的操作:

          清單1 putIfAbsent的等價操作

          if (!map.containsKey(key))
             return map.put(key, value);
          else
             return map.get(key);

          在前面的章節中提到過,連續兩個或多個原子操作的序列并不一定是原子操作。比如上面的操作即使在Hashtable中也不是原子操作。而putIfAbsent就是一個線程安全版本的操作的。

          有些人喜歡用這種功能來實現單例模式,例如清單2。

          清單2 一種單例模式的實現

          package xylz.study.concurrency;

          import java.util.concurrent.ConcurrentHashMap;
          import java.util.concurrent.ConcurrentMap;

          public class ConcurrentDemo1 {

              private static final ConcurrentMap<String, ConcurrentDemo1> map = new ConcurrentHashMap<String, ConcurrentDemo1>();
              private static ConcurrentDemo1 instance;
              public static ConcurrentDemo1 getInstance() {
                  if (instance == null) {

                      map.putIfAbsent("INSTANCE", new ConcurrentDemo1());

                      instance = map.get("INSTANCE");
                  }
                  return instance;
              }

              private ConcurrentDemo1() {
              }

          }

          當然這里只是一個操作的例子,實際上在單例模式文章中有很多的實現和比較。清單2 在存在大量單例的情況下可能有用,實際情況下很少用于單例模式。但是這個方法避免了向Map中的同一個Key提交多個結果的可能,有時候在去掉重復記錄上很有用(如果記錄的格式比較固定的話)。

           

          boolean remove(Object key,Object value)

          只有目前將鍵的條目映射到給定值時,才移除該鍵的條目。這等價于清單3 的操作。

          清單3 remove(Object,Object)的等價操作

          if (map.containsKey(key) && map.get(key).equals(value)) {
             map.remove(key);
             return true;
          }
          return false;

          由于集合類通常比較的hashCode和equals方法,而這兩個方法是在Object對象里面,因此兩個對象如果hashCode一致,并且覆蓋了equals方法后也一致,那么這兩個對象在集合類里面就是“相同”的,不管是否是同一個對象或者同一類型的對象。也就是說只要key1.hashCode()==key2.hashCode() && key1.equals(key2),那么key1和key2在集合類里面就認為是一致,哪怕他們的Class類型不一致也沒關系,所以在很多集合類里面允許通過Object來類型來比較(或者定位)。比如說Map盡管添加的時候只能通過制定的類型<K,V>,但是刪除的時候卻允許通過一個Object來操作,而不必是K類型。

          既然Map里面有一個remove(Object)方法,為什么ConcurrentMap還需要remove(Object,Object)方法呢?這是因為盡管Map里面的key沒有變化,但是value可能已經被其他線程修改了,如果修改后的值是我們期望的,那么我們就不能拿一個key來刪除此值,盡管我們的期望值是刪除此key對于的舊值。

          這種特性在原子操作章節的AtomicMarkableReferenceAtomicStampedReference里面介紹過。

           

          boolean replace(K key,V oldValue,V newValue)

          只有目前將鍵的條目映射到給定值時,才替換該鍵的條目。這等價于清單4 的操作。

          清單4 replace(K,V,V)的等價操作

          if (map.containsKey(key) && map.get(key).equals(oldValue)) {
             map.put(key, newValue);
             return true;
          }
          return false;

           

          V replace(K key,V value)

          只有當前鍵存在的時候更新此鍵對于的值。這等價于清單5 的操作。

          清單5 replace(K,V)的等價操作

          if (map.containsKey(key)) {
             return map.put(key, value);
          }
          return null;

          replace(K,V,V)相比replace(K,V)而言,就是增加了匹配oldValue的操作。

           

          其實這4個擴展方法,是ConcurrentMap附送的四個操作,其實我們更關心的是Map本身的操作。當然如果沒有這4個方法,要完成類似的功能我們可能需要額外的鎖,所以有總比沒有要好。比如清單6,如果沒有putIfAbsent內置的方法,我們如果要完成此操作就需要完全鎖住整個Map,這樣就大大降低了ConcurrentMap的并發性。這在下一節中有詳細的分析和討論。

          清單6 putIfAbsent的外部實現

          public V putIfAbsent(K key, V value) {
              synchronized (map) {
                  if (!map.containsKey(key)) return map.put(key, value);
                  return map.get(key);
              }
          }

           

          參考資料:

           



          ©2009-2014 IMXYLZ |求賢若渴
          posted on 2010-07-19 15:25 imxylz 閱讀(24650) 評論(8)  編輯  收藏 所屬分類: Java Concurrency

          評論

          # re: 深入淺出 Java Concurrency (16): 并發容器 part 1 ConcurrentMap (1)[未登錄] 2010-07-19 18:05 denis
          uml圖很漂亮,有點rational rose風格,但是好像又有點區別。請問您用的uml軟件是?  回復  更多評論
            

          # re: 深入淺出 Java Concurrency (16): 并發容器 part 1 ConcurrentMap (1) 2010-07-19 18:12 xylz
          @denis
          Enterprise Architect 7.5  回復  更多評論
            

          # re: 深入淺出 Java Concurrency (16): 并發容器 part 1 ConcurrentMap (1)[未登錄] 2010-07-19 18:14 denis
          @xylz
          唉,果然,可惜要破解。  回復  更多評論
            

          # re: 深入淺出 Java Concurrency (16): 并發容器 part 1 ConcurrentMap (1) 2011-01-03 23:57 fordybeach
          map.put第一次返回的是null
          所以map.putIfAbsent第一次也是返回的null,那個單例模式第一次就返回的是null  回復  更多評論
            

          # re: 深入淺出 Java Concurrency (16): 并發容器 part 1 ConcurrentMap (1) 2011-01-04 13:02 xylz
          @fordybeach
          已經改正過來了,多謝多謝,你太仔細了  回復  更多評論
            

          # re: 深入淺出 Java Concurrency (16): 并發容器 part 1 ConcurrentMap (1) 2012-08-02 11:50 殘陽
          2啊,因為Hashtable是一個單詞啊,并非hash和table的聯合。。。  回復  更多評論
            

          # re: 深入淺出 Java Concurrency (16): 并發容器 part 1 ConcurrentMap (1) 2013-03-26 14:22 kkkkkkkkk
          Hashtable是集成Ojbect,lz看清楚了再寫的,不要誤導人的。  回復  更多評論
            

          # re: 深入淺出 Java Concurrency (16): 并發容器 part 1 ConcurrentMap (1) 2014-12-16 10:27 卓洛
          @kkkkkkkkk
          是你沒看清楚吧  回復  更多評論
            


          ©2009-2014 IMXYLZ
          主站蜘蛛池模板: 隆安县| 增城市| 芷江| 望都县| 彭阳县| 孝感市| 纳雍县| 普兰店市| 伊川县| 湟中县| 财经| 芮城县| 灯塔市| 元氏县| 富蕴县| 高邑县| 裕民县| 阳原县| 长治县| 察雅县| 剑阁县| 济南市| 天门市| 张家川| 望城县| 横峰县| 合阳县| 涪陵区| 榆社县| 武汉市| 塔城市| 青田县| 墨玉县| 渑池县| 体育| 西贡区| 庆云县| 许昌县| 古丈县| 乡城县| 恩施市|