首先是无论JDK1.7还是1.8都会产生的不安全因素:
- put元素的时候,A线程对一个元素进行hash计算桶的索引坐标,然而正当它准备插入元素的时候,B线程被调度并且被完整执行,如果这个时候A和B线程获得索引坐标是一样的,那么B会插入元素形成新的链表,但是A线程拿到的旧链表信息,所以A线程执行完后会覆盖B线程插入的元素。
另外,JDK1.7前是头插法,1.8后变为了尾插法,这里头插法会有一个安全隐患:
[3,A],[7,B]这两个元素我简化表示为A,B;问题就出现在扩容的时候:
- e = A A.next = B,这时候e为A,插在了index = 3 的位置,正准备插入B的时候(此时 e = B),另外的线程被调度且完整的完成了扩容操作,导致 B.next = A 这个时候原来的线程继续完成扩容的操作,插入B,然后往后遍历 e = B.next,然而引用信息已经被修改,B.next = A,所以该线程继续头插法插入A,A.next = B,如此便形成了一个环形链表,进入死循环。