多线程下应选用ConcurrentHashMap替代HashMap,其采用CAS加synchronized锁单桶机制,读无锁、写细粒度;低并发可用Collections.synchronizedMap;只读场景优先不可变Map;避免使用性能差的Hashtable。

Java 中的 HashMap 本身不加锁、不保证原子性,多线程直接读写会出问题——轻则数据丢失、size不准,重则链表成环导致 CPU 占满、程序卡死。解决的关键不是“修 HashMap”,而是换用更合适的线程安全替代方案,按场景选最匹配的一种。
高并发读写首选 ConcurrentHashMap
这是目前最推荐的方式。JDK 8+ 使用 CAS + synchronized 锁单个桶(而非整个 Map),读操作无锁、写操作细粒度,吞吐量远高于全局锁方案。
- 直接替换:用 ConcurrentHashMap
替代 new HashMap(),无需额外同步 - 复合操作更安全:优先使用 putIfAbsent()、computeIfAbsent()、merge() 等原子方法,避免先 get 再 put 的竞态
- 注意迭代器弱一致性:遍历时可能看不到其他线程刚插入的元素,但不会抛 ConcurrentModificationException,适合监控、统计等非强一致性场景
低并发或简单同步需求可用 Collections.synchronizedMap
适合并发不高、数据量小、且已有 HashMap 实例需快速包装的场景。它用一个对象锁住整个 Map,所有 public 方法都加了 synchronized。
- 包装方式:Collections.synchronizedMap(new HashMap())
- 必须手动同步遍历:entrySet()、keySet()、values() 返回的集合不是线程安全的,遍历时要显式加锁
- 性能瓶颈明显:高并发下易成为争用热点,不建议用于核心高频路径
只读配置类用不可变 Map
如果 Map 初始化后就不再修改(如加载配置、常量映射),根本杜绝写操作是最彻底的安全方式。
- JDK 9+ 推荐:Map.of("k1", "v1", "k2", "v2") 或 Map.copyOf(originalMap)
- 需要动态构建或更多功能:用 Guava 的 ImmutableMap.builder().put(...).build()
- 优势:线程安全、内存友好、不可篡改,还能防止误操作
尽量避开 Hashtable
虽然它是线程安全的,但所有方法都用 synchronized(this),锁粒度太大,性能差;且不支持 null 键/值,API 过时,仅建议用于兼容老系统。
- 不要因为“它自带 synchronized”就默认选它
- 现代项目中基本被 ConcurrentHashMap 全面取代
- 若已有代码在用,可逐步迁移,无需强求保留
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











