Hello,大家好,我是小米,一个热爱技术的程序大哥哥!今天咱们聊点有深度的——Java面试中常被问到的ConcurrentHashMap底层实现原理。相信这个问题对许多社招面试的小伙伴来说不陌生,答得好绝对能让面试官眼前一亮!不卖关子了,咱们开讲吧~

开场故事:一次猝不及防的面试

先来分享一个我朋友小明的故事。小明是个五年Java开发工程师,最近跳槽准备面试。一场技术面试中,面试官突然发问:

“说说ConcurrentHashMap的实现原理吧,具体点!”

小明心里一惊:平时用得多,线程安全、并发高效是它的卖点,可是“具体点”是啥意思?他说了几句,比如"它通过分段锁实现并发控制",但细节部分却卡住了,最后只拿到个“我们再联系你”的回复。

小明很懊悔,回家后就狠狠地研究了一番ConcurrentHashMap。这次我们一起来深入了解它的奥秘,避免类似的情况发生!

从痛点出发:HashMap线程不安全

在多线程环境中,HashMap的线程安全问题可能会让你大翻车。比如,多个线程同时对HashMap进行put操作,可能导致数据丢失、死循环,甚至程序崩溃。为了应对这些问题,Java提供了线程安全的并发集合类:ConcurrentHashMap

那它是怎么解决这些问题的呢?我们一层层剖析。

JDK 1.7:分段锁(Segment)

在JDK 1.7中,ConcurrentHashMap的底层结构是分段锁(Segment)+ HashEntry数组的组合。

1、数据结构

  • Segment:类似于一个小型的HashMap,每个Segment维护一部分数据,独立加锁。
  • HashEntry[]:每个Segment内部使用链表来存储键值对。

2、锁的设计

  • 每个Segment有自己的独立锁。
  • 多线程操作时,只需锁住对应的Segment,而不是锁住整个Map,从而实现了更高的并发性能

3、核心优点

  • 分而治之:通过将数据分成多个Segment,多个线程可以同时操作不同的Segment,从而减少锁竞争。
  • 线程安全:每个Segment内部有自己的锁,确保线程安全。

示例代码

别再被问懵了!ConcurrentHashMap面试攻略来了_红黑树

  • 16:默认分成16个Segment。
  • 0.75f:负载因子。
  • 16:每个Segment初始容量。

但1.7的实现有一个问题:Segment本质上还是一个锁,锁的粒度不够细,即使只操作某一个Segment,内部也有可能产生竞争。

JDK 1.8:CAS+Synchronized

在JDK 1.8中,ConcurrentHashMap做了很大的改进,摒弃了Segment,改用CAS(Compare-And-Swap)+ Synchronized的方式,进一步提升并发性能。

1、数据结构

  • Node[]:底层数据结构是一个Node数组,每个Node是一个键值对。
  • 链表/红黑树:当冲突过多(链表长度超过8)时,链表会转化为红黑树,提高查询效率。

2、无锁化(CAS)

  • 使用CAS操作来实现高效无锁的插入和更新。
  • CAS保证了原子性:只有当前值和预期值相等时,才会更新成功。

3、锁的优化

  • 对于复杂操作(如扩容、红黑树转换),使用Synchronized加锁,但锁的粒度更细,只锁住当前桶(Node[] 的某个位置)。

细节探讨:核心实现原理

1. put方法

put方法中涉及的关键步骤:

  • 定位桶的位置:通过key的hash值计算出在Node[]中的索引。
  • CAS插入节点
  • 如果该位置为空,使用CAS将新节点放入。
  • 如果已有节点,进入链表或树中寻找合适位置。
  • 链表转红黑树:当链表长度大于8时,转换为红黑树。

示例代码:

别再被问懵了!ConcurrentHashMap面试攻略来了_线程安全_02

  • 定位key1的桶。
  • CAS尝试插入,成功则返回。
  • 若冲突,则检查链表长度,必要时转换为红黑树。

2. get方法

get方法的实现非常高效,无需加锁:

  • 通过key的hash值找到对应桶
  • 遍历链表或树,找到匹配的key,返回value。

示例代码:

别再被问懵了!ConcurrentHashMap面试攻略来了_红黑树_03

  • 定位桶。
  • 无锁遍历链表或红黑树,快速返回。

3. 扩容机制

扩容是ConcurrentHashMap中较复杂的部分:

  • 触发条件:当元素数量超过Node[]的容量 * 负载因子时,触发扩容。
  • 扩容策略:将容量扩大为原来的2倍。
  • 迁移数据:分段迁移,每次迁移部分桶,避免一次性扩容导致性能抖动。

总结:ConcurrentHashMap的优点

  • 高并发性能:通过CAS和细粒度锁设计,减少锁竞争。
  • 线程安全:读操作无锁,写操作锁住最小粒度。
  • 扩容高效:分段扩容,避免阻塞整个结构。

面试官的考察重点

基本原理:能说明JDK 1.7和1.8的主要差异。

核心方法实现:熟悉put、get和扩容的关键逻辑。

线程安全机制:理解CAS和Synchronized的结合使用。

小米的小贴士

  • 理解CAS:多线程编程中的核心工具,推荐好好研究它的实现。
  • 源码阅读:花时间研究ConcurrentHashMap的源码,面试中可以脱颖而出。
  • 多线程实践:在实际项目中多用多线程技术,积累经验。

END

好啦,今天的分享就到这里。如果你觉得这篇文章对你有帮助,别忘了点赞、分享给朋友哦!我是小米,一个爱分享的程序大哥哥,我们下次再见啦~

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号软件求生,获取更多技术干货!