ConcurrentHashMap JDK7 vs JDK8
面试官问:"ConcurrentHashMap 在 JDK7 和 JDK8 里分别是怎么实现的?"
候选人小李答:"JDK7 用分段锁,JDK8 用 CAS + synchronized。"
面试官点点头:"JDK7 的分段锁是怎么实现的?"
小李支支吾吾答不上来。
面试官继续追问:"JDK8 为什么放弃了分段锁,改用 CAS + synchronized?"
小张彻底卡住了。
【面试官心理】 ConcurrentHashMap 是 Java 并发编程中最核心的类之一。JDK7 和 JDK8 的实现差异是面试高频考点。能说清楚分段锁原理、CAS+synchronized 优势、以及为什么 JDK9 之后继续优化的候选人,说明对 Java 并发有深入研究。
一、历史背景 🔴
1.1 为什么需要 ConcurrentHashMap
HashMap 不是线程安全的:
Hashtable 是线程安全的,但性能很差:
1.2 ConcurrentHashMap 的设计目标
【直观类比】
- Hashtable:一个人上公共厕所,其他人都得等
- ConcurrentHashMap:多个人上独立隔间,互不干扰
二、JDK 7 分段锁实现 🔴
2.1 分段锁结构
2.2 如何定位元素
2.3 put 方法
2.4 size 方法
JDK 7 的 size 方法是一个性能瓶颈,因为它需要遍历所有 Segment 并可能加锁。这就是为什么 JDK 8 改进了 size 的计算方式。
【学习小结】 JDK 7 ConcurrentHashMap 核心要点:
- 数组 + Segment(分段锁) + HashEntry(链表)
- 每个 Segment 是一把锁,锁住部分桶
- 定位元素:hash 高位确定 Segment,hash 低位确定桶
- 不同 Segment 可以并发操作
三、JDK 8 的改进 🔴
3.1 为什么放弃分段锁
JDK 7 的分段锁有几个问题:
3.2 JDK 8 的新结构
3.3 Node 结构
四、CAS 操作详解 🔴
4.1 什么是 CAS
CAS = Compare-And-Swap(比较并交换)
4.2 JDK 8 中的 CAS 使用
4.3 CAS 的 ABA 问题
ConcurrentHashMap 不直接处理 ABA 问题,因为它操作的是 key-value 对,不是简单的引用。但理解 ABA 问题对于理解 Java 并发很有帮助。
五、synchronized 在 JDK 8 中的应用 🟡
5.1 为什么要用 synchronized
CAS 虽然高效,但有局限性:
synchronized 可以锁住一段代码:
5.2 synchronized 的优化
JDK 8 对 synchronized 做了很多优化:
5.3 put 流程中的 synchronized
六、size 方法的改进 🟡
6.1 JDK 7 的 size 问题
6.2 JDK 8 的 CounterCell
6.3 CounterCell 设计思想
为什么这样设计?
- 避免所有线程都竞争同一个 baseCount
- 每个线程更新自己的 CounterCell
- 减少锁竞争,提高并发性能
七、JDK 7 vs JDK 8 核心对比 🟡
7.1 核心差异表
7.2 性能对比
7.3 ❌ 错误示范
候选人原话:"ConcurrentHashMap 是用 CAS 实现的,比 Hashtable 快很多。"
问题诊断:
- 过于简化,JDK 8 其实是 CAS + synchronized 的组合
- 没有说明为什么需要 synchronized
- 没有提到 JDK 7 和 JDK 8 的区别
面试官内心 OS:"这个候选人可能只是背过结论,没有深入理解。"
【面试官心理】 JDK 8 的 ConcurrentHashMap 是 CAS + synchronized 的组合,不是纯 CAS。能够解释为什么需要 synchronized 的候选人,说明真正理解了并发编程的复杂性。
八、生产避坑清单 🟡
8.1 ❌ 常见错误
8.2 原子操作示例
8.3 批量操作
九、面试高频追问 🟡
9.1 第一层追问
面试官:"JDK 8 为什么放弃了分段锁?"
候选人:...
正确回答:
- 锁粒度仍然较粗:同一 Segment 内的桶仍有竞争
- 结构复杂:Segment 继承 ReentrantLock,代码量大
- size() 需要遍历所有 Segment,可能加锁
- 并发级别固定为 16,不能动态调整
- 内存开销大
9.2 第二层追问
面试官:"ConcurrentHashMap 什么时候会加 synchronized?"
候选人:...
正确回答:
- 桶不为空且不是转发节点时
- 链表插入/删除
- 红黑树插入/删除
- synchronized 只锁单个桶,不影响其他桶
9.3 第三层追问
面试官:"CounterCell 是怎么减少锁竞争的?"
候选人:...
正确回答:
- 每个线程有自己的 CounterCell 槽位
- 线程更新时 CAS 更新自己的 CounterCell
- 不需要所有线程竞争同一个 baseCount
- 累加时遍历所有 CounterCell 求和
9.4 第四层追问
面试官:"ConcurrentHashMap 和 Collections.synchronizedMap 有什么区别?"
候选人:...
正确回答:
- Collections.synchronizedMap:所有操作都加 synchronized(全局锁)
- ConcurrentHashMap:CAS + synchronized 局部锁,锁粒度更细
- 并发性能:ConcurrentHashMap > Collections.synchronizedMap
【学习小结】 ConcurrentHashMap 核心要点:
- JDK 7:Segment 分段锁,锁粒度较粗
- JDK 8:CAS + synchronized,锁粒度细化到单个桶
- CounterCell 分散计数,改进 size() 性能
- 红黑树支持,解决哈希碰撞攻击
- key/value 不能为 null
- 复合操作需要用原子方法或加锁