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
          主站蜘蛛池模板: 广汉市| 孙吴县| 富宁县| 武威市| 崇左市| 泾源县| 成武县| 巨野县| 土默特左旗| 剑河县| 阜宁县| 仙桃市| 达拉特旗| 普格县| 永清县| 三门县| 荔浦县| 鄂伦春自治旗| 玉门市| 弋阳县| 于都县| 高安市| 花莲县| 金山区| 萝北县| 西盟| 息烽县| 夹江县| 呼和浩特市| 鄂尔多斯市| 连山| 桂阳县| 新民市| 五莲县| 吉安县| 江津市| 弥渡县| 和平区| 郸城县| 贵溪市| 高要市|