JDK1.7 及之前版本,在多线程环境下,HashMap 扩容时会造成死循环和数据丢失的问题。
死循环问题:
这是由于当一个桶位中有多个元素需要进行扩容时,多个线程同时对链表进行操作,头插法可能会导致链表中的节点指向错误的位置,从而形成一个环形链表,使得查询元素的操作陷入死循环而无法结束
为解决这个问题,JDK1.8版本的HashMap 采用了尾插法而不是头插法来避免链表倒置,使得插入的节点永远都是放在链表的末尾,避免了链表中的环形结构。
但是还是不建议在多线程下使用
HashMap
,因为多线程下使用HashMap
还是会存在数据覆盖的问题。并发环境下,推荐使用ConcurrentHashMap
。
数据丢失问题在 JDK1.7 和 JDK 1.8 中都存在,这里以 JDK1.8 为例。
JDK1.8 后,在 HashMap 中,多个键值对可能会被分配到同一个桶,并以链表或红黑树的形式存储。多个线程对 HashMap 的 put 操作会导致线程不安全,具体来说会有数据覆盖的风险。
举个例子:
- 两个线程 1,2 同时进行 put 操作,并且发生了哈希冲突(hash 函数计算出的插入下标是相同的)
- 不同的线程可能在不同的时间片获得 CPU执行的机会,当前现场 1 执行完哈希冲突判断后,由于时间片耗尽挂起。线程 2 先完成了插入操作。
- 随