xylz,imxylz

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

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

          大家都知道HashMap不是線程安全的,但是大家的理解可能都不是十分準確。很顯然讀寫同一個key會導致不一致大家都能理解,但是如果讀寫一個不變的對象會有問題么?看看下面的代碼就明白了。

           1 import java.util.HashMap;
           2 import java.util.Map;
           3 import java.util.Random;
           4 import java.util.concurrent.ExecutorService;
           5 import java.util.concurrent.Executors;
           6 import java.util.concurrent.TimeUnit;
           7 import java.util.concurrent.atomic.AtomicInteger;
           8 
           9 public class HashMapTest2 {
          10     static void doit() throws Exception{
          11         final int count = 200;
          12         final AtomicInteger checkNum = new AtomicInteger(0);
          13         ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(100);
          14         //
          15         final Map<Long, String> map = new HashMap<Long, String>();
          16         map.put(0L"www.imxylz.cn");
          17         //map.put(1L, "www.imxylz.cn");
          18         for (int j = 0; j < count; j++) {
          19             newFixedThreadPool.submit(new Runnable() {
          20                 public void run() {
          21                     map.put(System.nanoTime()+new Random().nextLong(), "www.imxylz.cn");
          22                     String obj = map.get(0L);
          23                     if (obj == null) {
          24                         checkNum.incrementAndGet();
          25                     }
          26                 }
          27             });
          28         }
          29         newFixedThreadPool.awaitTermination(1, TimeUnit.SECONDS);
          30         newFixedThreadPool.shutdown();
          31         
          32         System.out.println(checkNum.get());
          33     }
          34     
          35     public static void main(String[] args) throws Exception{
          36         for(int i=0;i<10;i++) {
          37             doit();
          38             Thread.sleep(500L);
          39         }
          40     }
          41 }
          42 

          結果一定會輸出0么?結果卻不一定。比如某一次的結果是:

           

          0
          3
          0
          0
          0
          0
          9
          0
          9
          0

           

          查看了源碼,其實出現這個問題是因為HashMap在擴容是導致了重新進行hash計算。

          在HashMap中,有下面的源碼:

           
           1     public V get(Object key) {
           2         if (key == null)
           3             return getForNullKey();
           4         int hash = hash(key.hashCode());
           5         for (Entry<K,V> e = table[indexFor(hash, table.length)];
           6              e != null;
           7              e = e.next) {
           8             Object k;
           9             if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
          10                 return e.value;
          11         }
          12         return null;
          13     }


          在indexOf中就會導致計算有偏移。

          1 static int indexFor(int h, int length) {
          2         return h & (length-1);
          3     }

           

          很顯然在Map的容量(table.length,數組的大小)有變化時就會導致此處計算偏移變化。這樣每次讀的時候就不一定能獲取到目標索引了。為了證明此猜想,我們改造下,變成以下的代碼。

          final Map<String, String> map = new HashMap<String, String>(10000);

          執行多次結果總是輸出:

           

          0
          0
          0
          0
          0
          0
          0
          0
          0
          0

           

          當然了如果只是讀,沒有寫肯定沒有并發的問題了。改換Hashtable或者ConcurrentHashMap肯定也是沒有問題了。


          這里有一篇完整分析HashMap原理的文章。 __ by xylz 2010.07.20


          ©2009-2014 IMXYLZ |求賢若渴
          posted on 2009-12-18 18:20 imxylz 閱讀(6172) 評論(2)  編輯  收藏 所屬分類: J2EE技術

          評論

          # re: HashMap的讀寫并發 2009-12-31 11:27 bing
          很有意思,謝謝!  回復  更多評論
            

          # re: HashMap的讀寫并發[未登錄] 2010-07-20 15:38 test
          是hashcode的index變了,而不是hashcode變了,0L的hashcode應該會始終不變的。  回復  更多評論
            


          ©2009-2014 IMXYLZ
          主站蜘蛛池模板: 内江市| 息烽县| 松潘县| 巴林左旗| 和田市| 北票市| 永仁县| 克山县| 汶上县| 黄陵县| 丹阳市| 东明县| 班戈县| 荃湾区| 霸州市| 永和县| 四平市| 漳平市| 芜湖县| 澜沧| 汽车| 吉隆县| 兴仁县| 玉山县| 巴彦县| 海晏县| 遂平县| 乐昌市| 武平县| 伊吾县| 泽州县| 湘阴县| 宜君县| 平湖市| 三江| 和政县| 大安市| 寿宁县| 哈尔滨市| 大邑县| 蓝田县|