java8怎么解决java7中HashMap 多线程扩容发生死链的情况

首先 先仔细看一下关于扩容的问题:http://www.cnblogs.com/wang-meng/p/7582532.html
我这里截取文章中的一部分内容:
在HashMap的源码中,resize这个函数中调用了transfer(),该方法源码 见下图:
在这里插入图片描述

单线程扩容:

假设:hash算法就是简单的key与length(数组长度)求余。
hash表长度为2,如果不扩容, 那么元素key为3,5,7按照计算(key%table.length)的话都应该碰撞到table[1]上

扩容:hash表长度会扩容为4
重新hash,key=3 会落到table[3]上(3%4=3), 当前e.next为key(7), 继续while循环
重新hash,key=7 会落到table[3]上(7%4=3), 产生碰撞, 这里采用的是头插入法,所以key=7的Entry会排在key=3前面(这里可以具体看while语句中代码)
当前e.next为key(5), 继续while循环
重新hash,key=5 会落到table[1]上(5%4=3), 当前e.next为null, 跳出while循环, resize结束在这里插入图片描述
在这里插入图片描述
然后线程1被唤醒了:

  1. 执行e.next = newTable[i],于是 key(3)的 next 指向了线程1的新 Hash 表,因为新 Hash 表为空,所以e.next = null,

  2. 执行newTable[i] = e,所以线程1的新 Hash 表第一个元素指向了线程2新 Hash 表的 key(3)。好了,e 处理完毕。

  3. 执行e = next,将 e 指向 next,所以新的 e 是 key(7)
    然后该执行 key(3)的 next 节点 key(7)了:

  4. 现在的 e 节点是 key(7),首先执行Entry<K,V> next = e.next,那么 next 就是 key(3)了

  5. 执行e.next = newTable[i],于是key(7) 的 next 就成了 key(3)

  6. 执行newTable[i] = e,那么线程1的新 Hash 表第一个元素变成了 key(7)

  7. 执行e = next,将 e 指向 next,所以新的 e 是 key(3)
    在这里插入图片描述
    然后又该执行 key(7)的 next 节点 key(3)了:

  8. 现在的 e 节点是 key(3),首先执行Entry<K,V> next = e.next,那么 next 就是 null

  9. 执行e.next = newTable[i],于是key(3) 的 next 就成了 key(7)

  10. 执行newTable[i] = e,那么线程1的新 Hash 表第一个元素变成了 key(3)

  11. 执行e = next,将 e 指向 next,所以新的 e 是 key(7)
    这时候的状态如图所示:
    在这里插入图片描述
    很明显,环形链表出现了!!当然,现在还没有事情,因为下一个节点是 null,所以transfer()就完成了,等put()的其余过程搞定后,HashMap 的底层实现就是线程1的新 Hash 表了。

因为在 JDK1.7 中采取的是头插法,遍历一个节点就插入一个节点到新的哈希桶数组,所以才会导致出现循环链表。但 JDK1.8 中是采用**两个头结点来保持旧链表的引用,直到该索引处对应的链表全部遍历完之后再分别把头结点放在新的哈希桶数组对应的位置。**而不是遍历一个节点就插入一个节点到新的哈希桶数组。所以不会出现死链。
在这里插入图片描述
在这里插入图片描述

转自:《理解Java7和8里面HashMap+ConcurrentHashMap的扩容策略》https://blog.csdn.net/u010454030/article/details/82458413

HashMap java8 其他相关的详细介绍可以见:https://www.jianshu.com/p/4177dc15d658
也可以查看本人之前的相关文章。

猜你喜欢

转载自blog.csdn.net/mulinsen77/article/details/89602619