Sunday面试指南

HashMap 和 ConcurrentHashMap 有什么区别?并发读写为什么不能混用?

🧑‍💻 面试官:HashMap 换成 ConcurrentHashMap,并发问题就都解决了吗?

🙋‍♂️ 我:它是线程安全的,所以读写应该都没有问题了。

🧑‍💻 面试官:两个线程同时先检查 key 不存在,再放入一个对象,会发生什么?

🙋‍♂️ 我:既然每次读写安全,应该只会创建一次吧。

🧑‍💻 面试官:那扣库存要同时更新两个 key,它会帮你保证这两次修改一起成功吗?

「容器安全」不等于「业务操作安全」。先问一次操作的边界有多大,再决定靠 Map 方法还是靠额外协调。

面试速答(60 秒版)

HashMap 不提供并发读写的同步保证。多个线程共享并修改时,需要外部同步或选择合适的并发容器。

ConcurrentHashMap 支持安全的并发访问,但它不是给整个 Map 加一把大锁。读取通常不阻塞,更新按其实现协调。它也不允许 null 键和值。

选用它以后,仍然要区分单次操作和复合操作。先 get 再 put,并不会自动变成一个原子操作;按同一个 key 创建或更新,可以考虑 putIfAbsent、computeIfAbsent、compute 等。涉及多个 key 的业务约束,或 value 内部的可变状态,仍然需要另行保证。

并发 Map 的保证不自动覆盖跨 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(历史校招面经)

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历