现在我们通过源码看下为什么 jdk1.7 中Hashmap 在多线程的情况下扩容会造成死循环,我们直接看源码:
我们简单解释下, 在Hashmap放入元素的时候会有一个 addEntry 的方法, 里面会判断当 Hashmap 中的元素个数达到threshold 阈值的时候, 会进入resize 扩容方法,长度为原来的2倍。在 resize 中我们可以看到首先创建了一个 Entry 数组 newTable,长度为newCapacity,然后会进入 transfer 方法将原来数组中的元素转换到新数组 newTable中,现在我们着重看下transfer 方法:
方法不长,总共只有15行代码,但却是造成死循环的关键地方。 红框中的代码就是造成HashMap死循环的原因,现在我们假设原数组中的第一个元素为一个链表 2–>4,如下图所示:
2--->4 |
现在我们复现一下它是如何转换元素的,假设rehash 为 false,我们看下while 中的第一次遍历是什么样的:
上图可以看到e 为元素2,e.next 为元素 4,而 newTable 为新数组,所以 newTable[i] 是为null 的,while中第一次遍历结束后,我们看下新数组的元素存储情况:
2--->null |
现在我们看下while 中的第二次遍历:
上图中可以看到 e 为元素4 ,而由于元素4和元素2是在原数组的同一个位置中的,所以他们的 e.hash 的值是一样的,这样算出来的下标 **i 也是一样的,**这样 newTable[i] 就是上一个元素 2。 所以 e.next=2, newTable[i]=4 , 继续往下走,next 为null则循环结束。我们继续看下新数组中的元素存储情况:
4--->2 |
可以看到链表中的元素被反转了过来,这是因为jdk1.7 中HashMap 在扩容时转换元素的时候,若同一个位置有多个元素,则会采用头插法方式将元素放入新数组,也就是元素始终会在原来元素的头部插入。
我们假设现在有A,B2个线程,A 线程第一次循环结束后,在执行到592行的时候被挂起了。现在B线程进来执行完了2次循环,将4指向2,B结束,A继续执行,就出现2–>4, 4–>2的死循环情况。
我们继续看下jdk1.8 Hashmap 在多线程的情况下扩容的操作源码:
话不多说,直接看 resize 方法:
在查看resize方法时,突然觉得有可以优化的点,于是写了这篇resize优化,虽然推理过程不准确的,但整个思考过程比较完整,有兴趣的可以看看。
由于处于同一位置的哈希值大部分情况是不同的,而 e.hash & oldCap 只会有2种情况,等于0和不等于0,所以采用 loHead,loTail 和 hiHead,hiTail 这四个变量进行区分,而 loTail.next = null; 和 hiTail.next = null; 这2行的效果一样都是为了避免数据重复。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。
因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!
由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频
如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
,每个节点里面都包含素材文档、项目源码、讲解视频**
如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
[外链图片转存中…(img-llnSWsxN-1712585489145)]