concurrenthashmap 采用 cas + 桶级 synchronized 实现细粒度锁,volatile 保障可见性;size() 返回估计值;get() 弱一致性无锁;禁止 null 键值以消除语义歧义;相比 hashtable 全表锁,显著提升高并发吞吐量。

ConcurrentHashMap 是 Java 并发面试的绝对核心,复习不能只背“线程安全”,要抓住它在内存模型、锁粒度、语义约束、业务适配四个层面的设计逻辑。下面按真实面试节奏组织重点。
理解 JDK 1.8 的底层实现机制
面试官常从“为什么不用 Hashtable”切入,你要能立刻对比出本质差异:
- JDK 1.7 用 Segment 分段锁(16 段),JDK 1.8 彻底去掉 Segment,改用 CAS + 桶级 synchronized:只锁链表头或红黑树根节点,不是锁整个 map
- table 数组引用是 volatile 的,保证扩容后新数组对所有线程立即可见;Node 的 val 和 next 字段也是 volatile,支撑 get() 无锁但线程安全
- size() 返回的是近似值——它靠 baseCount + CounterCell[] 累加,不加锁更新,牺牲精确性换高性能
说清线程安全的边界在哪里
很多候选人误以为“ConcurrentHashMap 全操作都原子”,这是高频失分点。必须明确:
- get() 无锁,但不保证看到最新写入:弱一致性迭代器、读操作不阻塞写,也不等待写完成
- put() 单个键值对是线程安全的,但复合操作不是:比如 if (!map.containsKey(k)) map.put(k, v),存在竞态,必须用 computeIfAbsent 或 putIfAbsent
- null 键和 null 值被禁止:不是技术限制,而是为避免 get(null) 返回 null 时无法区分“键不存在”还是“值为 null”,这是语义清晰性的主动设计
能对比出它和同类容器的关键取舍
面试必问“ConcurrentHashMap vs Collections.synchronizedMap vs Hashtable”,回答要落到性能与语义权衡上:
- Hashtable 和 synchronizedMap 是全表锁,任意 put/get 都会串行化整个 map,吞吐量低
- ConcurrentHashMap 把锁拆到桶级别,99% 场景下读写互不阻塞,只有哈希冲突严重时才局部加锁
- 它的迭代器不抛 ConcurrentModificationException,但也不保证反映实时状态——这是用“读的模糊性”换“读的无锁性”
结合典型并发场景讲清楚怎么用
光懂原理不够,得落地。面试官喜欢追问“你项目里怎么用的”,准备 1–2 个真实映射:
- 缓存预热/本地计数:用 computeIfAbsent 初始化单例对象或统计维度,避免重复构建
- 防止缓存击穿:get + putIfAbsent 组合有空隙,必须升级为 computeIfAbsent,内部用 CAS 尝试 + synchronized 兜底
- 替代静态 Map 缓存配置:比 HashMap + 手动加锁更简洁,比 Guava Cache 更轻量(无过期/淘汰逻辑)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











