目录
情景再现
网络上很多文章说HashMap死循环都是理论分析,其一是没有自己实验过,甚至给出的实验程序都是错误的,不能再现死循环的BUG,其二是给出的示意图不够详细,很多细节忽略了让人难以理解,本文在前人总结的基础上摸索出了实现死循环的方法,记录成文。
死循环原因
我们首先思考一下在什么样的情况下HashMap会死循环,死循环的原因不外乎是在多线程的同时扩容,在JDK 1.7的HashMap中,当hash冲突时,采用头插法拉链表,所谓头插法,即在每次都在链表头部(即桶中)插入最后添加的数据
当触发扩容阈值准备扩容的时候,会循环旧桶中的每个元素,重新计算hash值然后再次分配到新的桶中,这个过程如果产生hash冲突,那也会采用头插法拉链表,循环的时候是从头到尾,而头插法插入的时候,尾巴上的元素反而会变成头元素,相当于逆序了
当一个线程A在扩容还未完成的时候,不巧的是失去了CPU时间片,不能获得执行机会,另外一个线程B抢先扩容完毕了,并且链表变成了逆序,而线程A还在按照顺序操作,造成指针混乱,于是出现死循环,当然这么说过于笼统也说不清楚,后文讲解原理的时候会详细到每个步骤!
实验环境
IDEA + JDK 1.7,自从Oracle改版以后需要登录才能下载,而且链接不是很好找,这里推荐一个下载站点:
http://java.sousou88.com/spec/oraclejdk.html
实验思路
既然要多线程才会实现,那么可以利用IDEA的多线程DEBUG操作将两个线程同时停在扩容操作处,然后让其中一个线程先扩容完,另外一个线程再次扩容造成死循环
程序代码
import java.io.IOException;
import java.util.HashMap;
/**
* 测试在JDK 1.7中 HashMap出现死循环
*/
public class Main {
/**
* 这个map 桶的长度为2,当元素个数达到 2 * 1.5 = 3 的时候才会触发扩容
*/
private static HashMap<Integer,String> map = new HashMap<Integer,Strin