HashMap的死循环(HashMap infinite loop)

仅为学习交流,如果有错,请指正,谢谢

HashMap是一个线程不安全的key-value的存储集合,也意味着它在多线程的环境中也存在很大的风险。

HashMap的存储结构:

通常来讲,HashMap是由一个数组和一个链表组成,在初始化的时候,HashMap会初始化一个数组table[],在不指定容量的情况下默认为16,负载系数为0.75,HashMap在put的时候会通过key的hash值来计算这个数组的下标,然后就把<key,value>这个存储集合存储在该下标的数组中,在查找时的复杂度为O(1),然而在Hash算法中,很有可能存在不同的key算出相同的值,HashMap就会把相同的值用链表来表示,这个时候就要遍历链表了,查找复杂度为O(n).
正是由于链表的存在,在多线程的环境下,共享链表,这就会变得不安全了。
什么时候链表会变得不安全呢?HashMap的容量是动态的,随着容量的增加而增量,在每次put的时候都会检查当前的容量是否满足,假设上述图片的容量为4,如果当前的容量大于4*0.75(负载因子),就会创建一个4*2的容量的新数组,将老的数组Copy到新的数组,然后所有的值就会重新hash,也就是rehash。
现在我们模拟两个线程下的rehash情况,我们有两个线程:Thread1,Thread2,我们先看看HashMap中的rehash方法 transfer().
1:   // tranfer()片段
2:   // 这是在resize()中调用的方法,resize()就是HashMap扩容的方法
3:   
4:   for (int j = 0; j < src.length; j++) {
5:     Entry  e = src[j];
6:    if (e != null) {
7:      src[j] = null;
8:      do {
9:      Entry next = e.next;  //假设线程1停留在这里就挂起了,线程2登场
10:     int i = indexFor(e.hash, newCapacity);
11:     e.next = newTable[i];
12:     newTable[i] = e;
13:     e = next;
14:   } while (e != null);
15:   }
16: } 

此时运行完Thread1:

此时Entry e是e1,Entry next = e.next中的next是next1,红色是还没有完成,指针指向步骤还没有开始。
现在Thread2登场了,Thread2运行完结构如下:

发现与Thread1的情况刚好反过来了,此时Entry e是e2,Entry next = e.next中的next是next2,是的,Thread2已经完成了指针指向操作了
        Entry  next = e.next;  
10:     int i = indexFor(e.hash, newCapacity);
11:     e.next = newTable[i];
12:     newTable[i] = e;
13:     e = next;//假设Thread2已经走了这里

这个时候Thread1要登场了,从 Entry  next = e.next;开始继续运行下去,此时在Thread2的影响下Thread1运行的结构已经变了


此时由于Thread2的影响,(key:2 ,value:b)已经指向了(key:1,value:a),而红线是Thread1接下来的 操作,完成指针指向操作,当Thread1完成时结构如下

这个时候你就会发现圆圈内形成了一个闭环,infinite loop就形成了!!!!

参考:  
http://pettyandydog.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值