hashmap的读写并发 -凯发k8网页登录

关注后端架构、中间件、分布式和并发编程

   :: 凯发k8网页登录首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  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 阅读(5971) 评论(2)     所属分类: j2ee技术
# re: hashmap的读写并发 2009-12-31 11:27
很有意思,谢谢!  回复  
  

# re: hashmap的读写并发[未登录] 2010-07-20 15:38
是hashcode的index变了,而不是hashcode变了,0l的hashcode应该会始终不变的。  回复  
  


©2009-2014
网站地图