【Java】HashMap线程安全吗?

发布于:2025-08-05 ⋅ 阅读:(13) ⋅ 点赞:(0)

这篇是关于HashMap是否是线程安全的讲解,希望对大家有所帮助,也请大佬们多多指教,欢迎点赞、评论!!

HashMap不是线程安全的。

它的设计初衷是为了单线程环境下的高效哈希表操作,内部没有任何同步机制。
(如synchronized锁或volatile关键字),在多线程并发修改时会导致数据不一致、死循环等问题。

一、典型不安全场景:多线程并发扩容导致链表成环(JDK1.7及以前)

JDK1.7中,HashMap的底层结构是 数组+链表,当元素数量超过阈值(capacity*loadFactor)时会触发扩容(resize)
核心步骤是将就数组的元素迁移到新数组中(transfer方法)

多线程并发扩容时,可能导致链表节点的引用关系被破坏,形成环形链表,后续执行get操作时会陷入无限循环,导致CPU飙升。

场景复现步骤:

1、初始状态:

假设HashMap容量为2,负载因子为0.75,阈值为1.已有一个元素key=0(哈希后 落在索引为0),此时再插入元素会触发扩容(容量变为4)。

2.并发扩容:

  • 线程A和线程B同时执行put方法,均触发扩容,进入transfer方法。
  • 线程A先执行部分迁移,将旧数组索引0的链表(假设只有key=0)迁移到新数组,此时链表节点的next引用暂时被修改。
  • 线程B暂停后恢复执行,继续迁移时复用了线程A修改后的next引用,导致链表节点之间形成循环(比如:节点A的next引用指向节点B,节点B的next引用指向节点A)。

3.后果:

当后续线程执行get操作查询环形链表中的元素时,会陷入无限循环,无法退出。

二、其他线程不安全的场景:

1.数据覆盖:

多个线程同时执行put操作时,若两个线程计算出的哈希索引相同时,且同时判断该位置为空,会导致后执行的线程覆盖先执行线程插入的数据,造成数据丢失。

// 示例代码(多线程执行)
HashMap<String, Integer> map = new HashMap<>();

// 线程1
new Thread(() -> map.put("key", 1)).start();
// 线程2
new Thread(() -> map.put("key", 2)).start();

// 最终map中"key"的值可能是1或2,也可能出现异常(取决于执行时序)

2.size计数不准:

HashMap的size字段用于记录元素数量,多线程并发put/remove时,size++/size--操作不是原子性的(可能被打断),导致最终size与实际元素数量不符。

三、总结:

1.HashMap的线程不安全本质是缺乏同步机制,无法保证多线程操作的原子性、可见性和有序性,导致内部数据结构(数组、链表/红黑树)被破坏或数据不一致。

2.若需在多线程环境中使用哈希表,应该选择线程安全的替代类,如ConcurrentHashMap(JDK1.5+,高效并发)或、Collection.synchronizedMap(new HashMap<>())(全表加锁,效率较低)。


网站公告

今日签到

点亮在社区的每一天
去签到