hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝
HashMap 不是线程安全的,在多线程环境下使用可能导致数据不一致、死循环等问题。以下是详细分析:
数据覆盖(Lost Updates)
put
操作,且键的哈希值相同。// 线程A和B同时执行
map.put(key1, value1); // 若两个线程的 key1 哈希到同一桶且桶为空
map.put(key1, value2); // 最终可能只有 value2 被保留
链表成环(Infinite Loop)
get
操作触发死循环。Size 计算错误
size
字段的自增操作(size++
)非原子性,多线程并发修改可能导致最终值小于实际插入数。哈希表状态不一致
put
、resize
等操作涉及多个步骤(如哈希计算、链表遍历、节点插入),未加锁导致中间状态暴露。// 线程A和B并发执行以下代码
public void unsafePut() {
Map<Integer, Integer> map = new HashMap<>(2);
for (int i = 0; i < 10000; i++) {
new Thread(() -> map.put(ThreadLocalRandom.current().nextInt(), 1)).start();
}
}
使用线程安全容器
ConcurrentHashMap
:分段锁(JDK 1.7)或 CAS + synchronized(JDK 1.8+),保证高并发下的安全性和性能。Collections.synchronizedMap
:通过同步方法包装 HashMap,但性能较低。显式同步控制
Map<String, String> syncMap = new HashMap<>();
// 每次操作时加锁
synchronized (syncMap) {
syncMap.put(key, value);
}
避免共享状态
方案 | 线程安全 | 性能 | 适用场景 |
---|---|---|---|
HashMap | 否 | 高 | 单线程或只读多线程环境 |
ConcurrentHashMap | 是 | 中高 | 高并发读写场景 |
synchronizedMap | 是 | 低 | 低并发场景,需兼容旧代码 |
ConcurrentHashMap
,或在必要时使用显式同步。