HashMap线程安全问题和死循环(Java 7)?
请解释Java 7中HashMap在多线程并发put时可能导致的**死循环(CPU 100%)**问题。说明头插法在多线程扩容(rehash)时如何形成循环链表,以及Java 8如何通过尾插法解决此问题。
回答
屠龙少年
问题背景:Java 7 HashMap采用头插法(新节点插入链表头部)和懒扩容机制。
死循环形成过程(多线程扩容时):
- 线程A执行到transfer()的Entry next = e.next后暂停
- 线程B完成完整的扩容,链表元素迁移且顺序反转(头插法)
- 线程A继续执行,使用旧引用操作已反转的链表 → 形成循环链表
- 后续对死循环桶的get/put操作进入无限循环 → CPU 100%
关键原因:头插法 + 多线程同时rehash + 共享链表引用
Java 8的改进:
- 尾插法:新节点插入链表尾部,保留原顺序
- 扩容后元素位置要么在原位置,要么在原位置+oldCap(不需要反转链表)
- 虽然HashMap在多线程下仍有数据丢失、size不准确等问题,但不会死循环
解决方案:
- 使用ConcurrentHashMap(推荐)
- 使用Collections.synchronizedMap()
- 使用HashTable(不推荐)
注意:Java 8的HashMap虽然没有死循环问题,但仍有其他线程安全问题(数据覆盖等),多线程场景应使用ConcurrentHashMap。