HashMap 和 ConcurrentHashMap 有什么区别?并发读写为什么不能混用?
🧑💻 面试官:HashMap 换成 ConcurrentHashMap,并发问题就都解决了吗?
🙋♂️ 我:它是线程安全的,所以读写应该都没有问题了。
🧑💻 面试官:两个线程同时先检查 key 不存在,再放入一个对象,会发生什么?
🙋♂️ 我:既然每次读写安全,应该只会创建一次吧。
🧑💻 面试官:那扣库存要同时更新两个 key,它会帮你保证这两次修改一起成功吗?
「容器安全」不等于「业务操作安全」。先问一次操作的边界有多大,再决定靠 Map 方法还是靠额外协调。
面试速答(60 秒版)
HashMap 不提供并发读写的同步保证。多个线程共享并修改时,需要外部同步或选择合适的并发容器。
ConcurrentHashMap 支持安全的并发访问,但它不是给整个 Map 加一把大锁。读取通常不阻塞,更新按其实现协调。它也不允许 null 键和值。
选用它以后,仍然要区分单次操作和复合操作。先 get 再 put,并不会自动变成一个原子操作;按同一个 key 创建或更新,可以考虑 putIfAbsent、computeIfAbsent、compute 等。涉及多个 key 的业务约束,或 value 内部的可变状态,仍然需要另行保证。

知识点详解:线程安全到底保护了哪一段操作
先看两个线程如何把检查结果用错
假设我们用一个共享 Map 缓存用户资料。线程 A、B 都先查询用户 42,结果都发现没有缓存。于是它们各自查询数据库,再各自写回。
即使这两次查询 Map 和两次写入 Map 都是安全的,也不代表「发现没有 → 创建 → 保存」这整个过程只有一个线程执行。中间仍然可以穿插别人的操作。
computeIfAbsent 把按 key 计算缺失值放到容器提供的原子操作里。但计算函数应该简短,不能递归地修改同一个 Map,也不适合塞进长时间阻塞的远程调用。耗时加载还要考虑请求合并、超时和失败缓存。
两个容器的区别不只是一把锁
HashMap 是普通哈希容器,适合不被并发修改的局部数据等场景。迭代时发现结构变化可能抛出异常,但这个机制不是并发正确性的保证,不能靠「没抛异常」证明安全。
ConcurrentHashMap 为并发访问设计。读取通常不需要等待写锁,遍历可以继续进行,但看到的不是整张表在某一个瞬间的完整快照。并发变化时的 size(),也不适合拿来保证「最多只能放入 100 个用户」这种严格约束。
另外,不要把早期 JDK 的分段锁结构当成所有版本的固定实现。面试先讲当前 API 保证;要谈内部结构,再说明具体 JDK 版本和源码。
Map 安全,不会顺便保护 value
假设 key 42 对应一个可变的购物车对象。Map 可以安全地把这个对象引用交给两个线程,但两个线程同时改购物车中的普通列表,列表不会因此获得线程安全。
处理方法取决于状态怎么使用:可以保存不可变对象,每次用原子更新替换;也可以在对象内部同步,或把修改集中到一个拥有者。不能只把外面的 Map 换掉,就宣布里面所有数据都安全。
同样,两个 key 一起更新也不在单 key 原子方法的保证范围内。余额转移等跨记录约束,应该交给明确的锁范围或数据库事务。
选择容器以后,还要验证竞争场景
读多写少不是免除同步的理由。关键是有没有多个线程共享可变状态,以及修改是否会重叠。只在启动阶段构建、之后可靠发布且不再修改的数据,与持续接收写入的缓存不是同一个问题。
测试时可以让两个线程同时到达「检查不存在」的位置,再一起继续,检查是否重复初始化。对于计数或限额,再检查大量竞争后最终值是否正确。压测只看有没有异常,很容易漏掉已经发生的丢失更新。
本题机制参考:JDK 25 HashMap、JDK 25 ConcurrentHashMap。
面试官继续追问
可以用 containsKey 加 put 实现唯一插入吗?
这两个调用之间存在竞争窗口。单 key 唯一插入要使用 putIfAbsent 等合适的原子操作,并检查返回结果。
ConcurrentHashMap 的遍历结果能当一致快照吗?
不能。遍历允许并发更新,可能反映不同时间的状态。需要业务快照时,应另行安排版本、锁或数据复制的边界。
为什么不能存 null?
这样缺失映射与显式 null 不会混在读取结果里,便于并发 API 表达无结果。具体保证以该容器 API 为准,不推广为所有 Map 的限制。
面试速记卡
- HashMap:不提供共享并发修改的同步保证。
- ConcurrentHashMap:访问安全,不等于复合业务原子。
- 同 key 复合修改使用适合的原子方法。
- 多 key 约束和 value 内部状态要另行协调。
- 遍历与 size 不是全表一致快照。
公司面试真题
真题根据求职者公开面经整理,题意经过概括,非逐字原话或公司官方题库;本文为 Sunday 的独立解析。
腾讯 · 后台开发 · 原帖未明确批次
HashMap 与 ConcurrentHashMap 有哪些区别?(题意整理)
腾讯后台开发面经(HR 部门) ↗
原帖编辑于 2025-02-19美团 · 开发(含 AI 项目追问) · 原帖未明确批次
HashMap 的底层结构是什么?(题意整理)
美团面经 ↗
原帖发布于 2025-09-09字节跳动 · 后端(番茄小说) · 实习
HashMap 的底层结构、扩容时机和复杂度是什么?(题意整理)
【字节跳动】番茄小说部门 后端实习 ↗
面试记录为 2021 年 1 月;原帖发布于 2022-04-29京东 · Java后台 · 校招
HashMap 怎样处理哈希冲突与扩容,ConcurrentHashMap 怎样保证线程安全?(题意整理)
京东 Java 后台三面凉经 ↗
原帖编辑于 2019-08-23(历史校招面经)