Java完整解析Map

ConcurrentHashMap用了悲观锁还是乐观锁?

它不是单一锁策略:读取尽量无锁,更新结合 CAS 与桶级同步,判断应落到具体操作和 JDK 版本。

面试Javajava核心题完整解析编辑精选

直接结论

不能简单回答“悲观锁”或“乐观锁”。Java 21 的 ConcurrentHashMap 在不同路径组合使用无锁读取、CAS 原子更新以及桶级 synchronized 协作:空桶插入通常尝试 CAS,发生桶冲突时会对桶头加同步锁,扩容时多个线程还可协助迁移。它的目标是让读取具有高并发度,让更新尽量缩小互斥范围,而不是为整个 Map 使用一把全局锁。面试时应强调这是并发算法的组合,而非二选一标签。

原理与步骤

get 根据 volatile 可见的表和节点字段查找,不为普通读取获取全局互斥锁;putIfAbsent、compute 等复合操作必须保证原子语义,在无竞争路径先使用 CAS,在有竞争或链表、树结构修改时进入局部同步。size 相关统计也通过分散计数降低热点。官方 API 承诺线程安全、检索通常不阻塞以及更新具有 happens-before 可见性,但不会承诺内部一定使用某一种锁或某个字段,因此内部机制要注明所讨论的 JDK 版本。

工程场景

在缓存索引、连接注册表等读多写少场景,ConcurrentHashMap 能避免用 Collections.synchronizedMap 把所有访问串行化。对于“若不存在则加载”的逻辑,应使用 computeIfAbsent 等原子 API,而不是先 containsKey 再 put;映射函数要短小,避免递归更新同一映射,也不要在其中执行不可控的慢 I/O。热点单键频繁更新仍可能形成竞争,需要考虑 LongAdder、分片或业务模型调整,而不是误以为并发容器能消除所有锁等待。

验证与边界

可以用多线程压力测试验证原子操作结果和吞吐量,但不能用一次基准就断言所有负载都无锁。迭代器是弱一致的,不等同于数据库快照;size 在并发变化时更适合监控估计,不应作为跨多个操作的事务条件。空键和值不被允许。回答还要区分 API 的并发保证与当前 OpenJDK 实现细节:未来 JDK 可改变 CAS、锁或数据结构,只要仍满足公共契约,因此生产代码不应反射依赖内部节点。

答题练习

  1. 1读取通常无全局锁
  2. 2空桶更新可走 CAS
  3. 3冲突更新使用桶级同步

常见错误

  • 把容器贴成纯乐观锁或纯悲观锁
  • 用 containsKey 加 put 模拟原子操作

可能追问

  • computeIfAbsent 有哪些使用约束
  • 弱一致迭代器意味着什么

来源记录

原始来源
小林coding
来源页面
Java集合面试题
最近收录
2026-07-04
官方复核
Oracle Java 官方文档