JDK7扩容机制形成的链表环过程详解
JDK7-HashMap-扩容bug
HashMap不是线程安全的key-value结构,在多线程环境下有可能存在多线程并发扩容,就有可能产生链表环,导致两个问题发生
- get数据时产生死循环
- 数据丢失问题
下面对产生链表环的问题就行分析,经典面试题。
单线程环境下扩容机制
单线程环境不会产生并发扩容,也就不会产生上述两个问题,先分析下正常场景下时如果扩容的将会为分析上述问题的分析做铺垫。
- 扩容代码
while(null != e) {
Entry<K,V> next = e.next;//第一行,线程1执行到此被调度挂起
int i = indexFor(e.hash, newCapacity);//第二行
e.next = newTable[i];//第三行
newTable[i] = e;//第四行
e = next;//第五行
}
扩容的代码说白了,就是将链表中的元素的引用指针的移动
- 扩容过程
假设由容量2扩容到4,假设扩容数组索引=key%4,扩容结束新数组结构如下
并发场景多线程扩容机制问题分析
假设有两个线程同时扩容
此时由于并发,执行完第一行代码,cpu分配给线程1的时间片到了,线程1睡眠
此时线程1被唤醒,从第二行代码执行
e和next指针变化
使用最新的next、e继续执行,新增了线程1扩容数组第一个节点对7的引用
更新next、e
继续执行迁移
红线部分就出现了链表环,当使用get方法时,正好命中在扩容数组的3号位置,遍历链表查询数据时走到链表环时无限循环,那么问题一就出现了。
另外由于元素3的next为null,导致5还未进行迁移,整个扩容工作就结束了,最终导致元素5丢失。
总结
在并发场景下的jdk7 hashMap存在风险,但是在jdk8中解决了扩容问题,解决方案的分析请看下一篇文章。