详解ConcurrentHashMap底层实现机制:大厂面试必考的高并发编程技巧

关注△mikechen的互联网架构△,10年+BAT架构经验。

详解ConcurrentHashMap底层实现机制:大厂面试必考的高并发编程技巧

大家好,我是mikechen|陈锐.

ConcurrentHashMap的实现原理基本上是各大厂商面试的必考,对于掌握高并发编程也有很大的参考价值。本文将详细讲解ConcurrentHashMap的底层实现机制@mikechen

01 哈希表 1.简介

哈希表是一种以键索引格式存储数据的结构。我们只需要输入要查找的值,也就是键,就可以找到对应的值。

哈希的思想非常简单。如果所有的key都是整数,那么可以用一个简单的无序数组来实现:以key为索引,value为其对应的值,这样就可以快速访问任意key的值。 。这是简单键的情况,我们将其扩展以处理更复杂类型的键。

2. 链式哈希表

链接哈希表基本上由一组链接列表组成。每个链表都可以看作一个“桶”,我们通过哈希将所有元素放入不同的特定桶中。

插入元素时,首先将其键传递给哈希函数(此过程称为哈希键)。函数通过哈希告诉元素属于哪个“桶”,然后将该元素插入到对应链表的头部。

在查找或删除元素时,我们首先用同样的方式找到该元素的“桶”,然后遍历对应的链表,直到找到我们想要的元素。

3、应用场景

我们所熟悉的缓存技术(如redis、memcached)的核心其实就是在内存中维护一张巨大的哈希表,以及我们所熟知的HashMap、CurrentHashMap等应用。

02 CurrentHashMap和HashMap的区别等1.HashMap

我们知道HashMap是线程不安全的。在多线程环境下,使用Hashmap进行put操作会造成死循环,导致CPU利用率接近100%,所以并发情况下不能使用HashMap。

2.哈希表

HashTable和HashMap的实现原理几乎是一样的。两者的区别在于:

但HashTable线程安全策略的实现成本太高,而且简单粗暴,所有get/put相关操作都是同步的,相当于给整个哈希表加了一把大锁。

多线程访问时,只要有一个线程访问或操作该对象,其他线程就只能阻塞,相当于将所有操作串行化,在竞争激烈的并发场景下性能会很差。

3.ConcurrentHashMap

它的诞生主要是为了解决并发环境下HashMap的不安全问题。

我们都知道Map一般是数组+链表结构(JDK1.8应该是数组+红黑树)。

ConcurrentHashMap避免了全局锁定,改为本地锁定操作,大大提高了并发环境下的操作速度。由于JDK1.7和1.8中ConcurrentHashMap的实现有很大不同,所以我们先来说说JDK 1.7和1.8的区别。

03 JDK1.7下CurrentHashMap实现

JDK1.7中,ConcurrentHashMap采用数组+Segment+段锁的方式实现。

1.Segment(段锁)

ConcurrentHashMap中的段锁称为Segment,它与HashMap的结构类似,即内部有一个Entry数组,数组中的每个元素是一个链表和一个ReentrantLock(Segment继承了ReentrantLock)。

2、内部结构

ConcurrentHashMap采用分段锁技术,将数据分成段进行存储,然后为每段数据分配一个锁。当一个线程占用锁访问一段数据时,其他段的数据也可以被其他线程访问。实现真正的并发访问。下图是ConcurrentHashMap的内部结构图:

从上面的结构我们可以了解到,在ConcurrentHashMap中定位一个元素的过程需要两次Hash操作。

第一个Hash定位Segment,第二个Hash定位元素所在链表的头部。

3、这种结构的优缺点

伤害

这种结构的副作用是Hash过程比普通HashMap要长。

益处

写操作时,只能锁定该元素所在的Segment,而不会影响其他Segment。这样,在最理想的情况下,ConcurrentHashMap最多可以支持同时对多个Segments进行写操作(只是这些写操作均匀分布在所有Segment上)。

通过这种结构,可以大大提高ConcurrentHashMap的并发能力。

04 JDK1.8的CurrentHashMap实现原理

JDK8中的ConcurrentHashMap参考了JDK8 HashMap的实现,采用数组+链表+红黑树的实现方式来设计。 CAS操作在内部被广泛使用。这里我简单介绍一下CAS。

CAS是compare and swap的缩写,也就是我们所说的比较和交换。 Cas是基于锁的操作,并且是乐观锁。 Java中的锁分为乐观锁和悲观锁。

悲观锁会锁定资源,直到先前获取锁的线程在下一个线程可以访问该锁之前释放该锁。

乐观锁采取了广泛的态度,以某种方式在不加锁的情况下处理资源,例如为记录添加版本来获取数据。与悲观锁相比,性能有很大提升。

在JDK8中,Segment被彻底抛弃,转而采用Node,其设计思想也不再是JDK1.7中的分段锁思想。

节点:保存key、value以及key的哈希值的数据结构。 value 和 next 都用 volatile 修改,以保证并发可见性。

class Node implements Map.Entry {
   final int hash;
   final K key;
   volatile V val;
   volatile Node next;
   //... 省略部分代码} 

Java 8 ConcurrentHashMap结构与Java 8 HashMap基本相同,但保证了线程安全。

在JDK8中ConcurrentHashMap的结构中,由于红黑树的引入,导致ConcurrentHashMap的实现非常复杂。我们都知道红黑树是一种性能非常好的二叉搜索树,其搜索性能为O(logN),但是实现过程也非常复杂,可读性很差,Doug

莉亚的思维能力确实不是常人能比的。早期完全采用链表结构时,Map的搜索时间复杂度是O(N)。在JDK8中,ConcurrentHashMap当链表长度大于一定阈值时,会将链表转换为红黑树。进一步提高其搜索性能。

05 CurrentHashMap实现原理总结

ConcurrentHashMap的数据结构由JDK1.7版本中的ReentrantLock+Segment+HashEntry变为JDK1.8版本中的synchronized+CAS+HashEntry+红黑树结构存储。

1.数据结构

取消了Segment段锁的数据结构,取而代之的是数组+链表+红黑树的结构。

2.保证线程安全机制

JDK1.7使用Segment的分段锁机制来实现线程安全,其中Segment继承自ReentrantLock。 JDK1.8使用CAS+Synchronized来保证线程安全。

3. 锁定粒度

以前是锁定需要数据操作的Segments,现在调整为锁定每个数组元素(Node)。

4. 将链表转换为红黑树

简化定位节点的哈希算法会带来弊端,加剧哈希冲突。因此,当链表节点数大于8时,链表将转换为红黑树进行存储。

5.查询时间复杂度

从原来的链表遍历O(n),变成红黑树的遍历O(logN)。

以上就是ConcurrentHashMap的实现原理的详细分析。欢迎在评论区留言交流或拓展。

我是麦克陈|陈锐.我关注【mikechen的互联网架构】,有10年+BAT架构技术来教我。

本文已同步我的技术博客,更新为我原创的《30W+字各大工厂架构技术合集》。

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

27阅读

java中冒号,java双冒号

2026年09月06日

25阅读

java中的->,java中的是什么意思

2026年09月06日

24阅读

java中的super用法,Java中super的说明

2026年09月06日

27阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

23阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

25阅读