hashmap线程不安全在哪里?
大家都知道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)?{?
在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肯定也是没有问题了。
?