在 Java 中,HashMap 是线程不安全的,主要体现在多线程并发操作时可能导致数据不一致、死循环、数据丢失等问题。以下是其线程不安全的核心原因和具体表现:
一、HashMap 线程不安全的表现
1. 数据覆盖(丢失)
- 场景:多线程同时调用
put()方法插入数据。 - 原因:当两个线程同时计算哈希值并定位到同一个桶(bucket)时,若该位置为空,可能发生数据覆盖。
// 假设线程A和线程B同时执行以下代码 if (table[bucket] == null) { table[bucket] = new Entry(key, value); // 可能被覆盖 }
2. 链表成环(JDK 1.7 的经典问题)
- 场景:多线程同时触发
resize()(扩容)。 - 原因:JDK 1.7 的 HashMap 使用头插法迁移链表,并发扩容时可能导致链表成环,后续的
get()操作触发死循环。// JDK 1.7 的扩容代码(简化) void transfer(Entry[] newTable) { for (Entry<K,V> e : table) { while (e != null) { Entry<K,V> next = e.next; // 线程A执行到这里挂起 e.next = newTable[bucket]; // 线程B先执行,导致链表成环 newTable[bucket] = e; e = next; } } }
3. size 不准确
- 场景:多线程同时调用
put()或remove()。 - 原因:
size变量是非原子操作(如size++),并发修改可能导致最终值错误。
二、HashMap 线程不安全的根本原因
1. 无同步机制
- HashMap 的设计目标是单线程高性能,未对多线程操作进行同步(如
synchronized或 CAS)。 - 关键操作(
put()、get()、resize())没有锁保护。
2. 可见性问题
- 多线程修改共享变量(如
table、size)时,未使用volatile关键字,可能导致一个线程的修改对其他线程不可见。
3. 复合操作非原子性
- 例如
put()操作包含多个步骤(计算哈希、定位桶、插入节点),多线程交叉执行时可能破坏内部结构。
三、JDK 1.8 的改进与残留问题
1. 改进:
- 链表改用尾插法(解决 JDK 1.7 扩容死循环问题)。
- 链表长度超过阈值时转为红黑树(减少哈希冲突时的查询时间)。
2. 残留问题:
- 数据覆盖:多线程同时插入相同哈希值的键,仍可能覆盖数据。
- 红黑树调整问题:并发修改红黑树结构可能导致树不平衡。
四、解决方案
1. 使用线程安全的替代类
ConcurrentHashMap:分段锁(JDK 1.7)或 CAS + synchronized(JDK 1.8),高并发性能更好。Collections.synchronizedMap():通过包装类对所有方法加锁(性能较差)。
2. 避免多线程直接操作 HashMap
- 限制为单线程使用,或通过副本、消息队列等方式隔离并发访问。
五、代码示例(JDK 1.8 的数据覆盖问题)
public class HashMapUnsafeDemo {
public static void main(String[] args) throws InterruptedException {
Map<String, Integer> map = new HashMap<>();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
map.put("key" + i, i);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
map.put("key" + i, i);
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Map size: " + map.size()); // 结果可能小于 1000
}
}
总结
| 问题类型 | 表现 | 根本原因 |
|---|---|---|
| 数据覆盖 | 部分键值对丢失 | 无锁导致复合操作非原子性 |
| 链表成环(JDK1.7) | get() 死循环,CPU 100% | 头插法扩容并发操作 |
| size 不准确 | size() 返回值错误 | 非原子操作 + 无可见性保证 |
核心结论:
HashMap 的线程不安全源于其设计未考虑并发场景,多线程环境下应优先使用 ConcurrentHashMap。
7138



被折叠的 条评论
为什么被折叠?



