(转帖)HashMap的读写并发 脏读 脏数据


HashMap的读写并发(http://chinaxxren.iteye.com/blog/794915)

  • 博客分类: 
  • J2SE
核心提示:大家都知道HashMap不是线程安全的,但是大家的理解可能都不是十分准确。很显然读写同一个key会导致不一致大家都能理解,但是如果读写一个不变的对象会有问题么?看看下面的代码就明白了。 1 import java.util.HashMap; 2 import java.util.Map; 3 import jav

大家都知道HashMap不是线程安全的,但是大家的理解可能都不是十分准确。很显然读写同一个key会导致不一致大家都能理解,但是如果读写一个不变的对象会有问题么?看看下面的代码就明白了。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->  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中,有下面的源码:

 
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->  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中就会导致计算有偏移。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 1  static  int indexFor( int h,  int length) {
2          return h & (length-1);
3     }

 

很显然在Map的容量(table.length,数组的大小)有变化时就会导致此处计算偏移变化。这样每次读的时候就不一定能获取到目标索引了。为了证明此猜想,我们改造下,变成以下的代码。

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> final Map<String, String> map =  new HashMap<String, String>(10000);

执行多次结果总是输出:

 

0
0
0
0
0
0
0
0
0
0

 

当然了如果只是读,没有写肯定没有并发的问题了。改换Hashtable或者ConcurrentHashMap肯定也是没有问题了。


另外一篇文章.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值