HashMap 的线程安全
·
HashMap 是线程安全的吗?多线程下会有什么问题?如何实现线程安全?
HashMap 不是线程安全的。
因为所有方法(如 put、get、resize 等)都没有使用 synchronized 或 加锁 进行保护。这些操作都不是原子性的
在多线程环境下,使用 HashMap 可能会出现以下问题:
- 扩容死循环:在 JDK 1.7 中,HashMap 使用头插法插入元素,当多个线程同时进行扩容操作时,可能会导致环形链表的形成,后续对同一哈希桶的
get操作会陷入死循环。为了解决这个问题,在 JDK 1.8 中采用了尾插法插入元素,保持了链表元素的顺序,避免了死循环的问题。 - 数据覆盖:当多个线程同时执行 put 操作时,如果它们计算出的索引位置相同,可能会造成前一个 key 被后一个 key 覆盖的情况,从而导致元素的丢失。
关于多线程安全的实现方案,可以采取以下措施:
-
如果并发要求不高或对遗留代码兼容,
-
Hashtable是 JDK 1.0 的遗留类,所有方法都用synchronized修饰,属于全表锁,(不允许null键值),性能较差,新代码中不建议使用。 -
Collections.synchronizedMap是 JDK 1.2 提供的包装类,同样采用全表锁(通过互斥对象),(允许null键值),但并发能力依旧有限,仅适合并发要求极低的场景。
-
-
如果是高并发场景,我通常会使用
ConcurrentHashMap。它针对并发做了专门优化:-
在 JDK 1.7 中采用分段锁(Segment),将数据分块,减少锁粒度,允许多个线程同时操作不同段。
-
在 JDK 1.8 中优化为 CAS + synchronized 保证线程安全性,锁的粒度细化到数组的每个桶(Node),只有在操作同一个哈希桶时才会发生锁竞争,并发性能是更优。
-
总之,在多线程环境下,尽量不要直接使用 HashMap,而是根据并发量的高低选择合适的封装类或 ConcurrentHashMap。
更多推荐




所有评论(0)