hashmap非线程安全,多线程读写易致数据丢失、死循环等;应依场景选concurrenthashmap(推荐)、collections.synchronizedmap(慎用)或避免共享;禁止单点加锁、手动锁遗漏及遍历时修改。

HashMap 本身不是线程安全的,直接在多线程中读写会导致数据丢失、size不准、死循环甚至 JVM 卡死。要“安全使用”,关键不是强行给 HashMap 加锁,而是选对方案——根据场景决定是替换、包装还是规避。
优先用 ConcurrentHashMap 替代
这是最常用也最推荐的做法。ConcurrentHashMap 不是简单加 synchronized,而是通过分段锁(Java 7)或 CAS + synchronized 锁单个桶(Java 8+)实现高并发读写。
- 读操作完全无锁,性能接近 HashMap;写操作只锁冲突的桶,不影响其他桶的并发访问。
- 支持原子方法如
putIfAbsent、computeIfAbsent、merge,能安全完成“查-改-写”逻辑,避免containsKey + put这类非原子组合。 - 迭代器是弱一致性(weakly consistent),不会抛
ConcurrentModificationException,也不会阻塞写入,但可能看不到最新写入或看到重复元素——这比崩溃或死循环更可控。
用 Collections.synchronizedMap 包装(慎选)
它把任意 Map(包括 HashMap)套上一层全方法同步的外壳,所有 public 方法都加了 synchronized。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 简单易用,适合低并发、读写不频繁、且已有 HashMap 代码难以重构的场景。
- 但所有操作串行化:哪怕两个线程操作完全不同 key,也会互相等待,吞吐量远低于 ConcurrentHashMap。
- 迭代仍需手动同步:
synchronized(map) { for (Entry e : map.entrySet()) { ... } },否则可能出错。
避免共享 HashMap 实例
如果只是临时计算、局部缓存或线程内专用结构,根本不需要共享。
- 每个线程用自己独立的 HashMap(如配合
ThreadLocal<hashmap></hashmap>),彻底避开竞争。 - 方法内新建 HashMap 存放中间结果,用完即弃,不跨线程传递引用。
- 若必须跨线程传数据,考虑转成不可变对象(如
Map.copyOf()或 Guava 的ImmutableMap),只读不改。
绝对不要做的几件事
这些看似“加点锁就安全”的做法,实际隐患很大:
- 只对
put()加 synchronized,却忽略get()、size()、迭代等操作——它们依然可能读到损坏结构或脏数据。 - 用
ReentrantLock手动保护整个 HashMap,但忘了在所有访问点(包括遍历、clear、entrySet)统一加锁,极易遗漏。 - 在 for-each 循环中边遍历边修改 HashMap —— 不管是否加锁,都会触发
ConcurrentModificationException(fail-fast 机制)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










