ConcurrentHashMap底层实现原理

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情

1. ConcurrentHashMap的整体架构

1.jpg 如图,这是ConcurrentHashMap在jdk1.8中的存储结构,它是由数据,单项链表,红黑树来构成,当我们去初始化一个ConcurrentHashMap实例的时候,默认会初始化一个长度等于16的数组,由于ConcurrentHashMap它的核心仍然是Hash表,所以必然会存在Hash冲突的问题,所以ConcurrentHashMap采用链式寻址的方式,来解决Hash表的冲突,当Hash冲突比较多的时候,会造成链表长度较长的问题,这种会使得ConcurrentHashMap中的一个数组元素的查询复杂度会增加,所以在jdk1.8里面,引入了红黑树的机制,当数组长度大于64并且链表长度大于等于8的时候,单向链表会转化成红黑树,另外随着ConcurrentHashMap的一个动态扩容,一旦链表的长度小于8,红黑树会退化成单向链表

2. ConcurrentHashMap的基本功能

2.jpg ConcurrentHashMap本质上是一个HashMap,因此功能和HashMap是一样的,但是ConcurrentHashMap在HashMap的基础上提供了并发安全的一个实现。并发安全的主要实现主要通过对于Node节点去加锁,来保证数据更新的安全性

3. ConcurrentHashMap在性能方面的优化

如何在并发性能和数据安全性之间去做好平衡,在很多地方都有类似的设计,比如说cpu的三级缓存,mysql的buffer_poor,Synchronized的一个锁升级等等,ConcurrentHashMap也做了类似一个优化,主要体现在几个方面

  1. 在jdk1.8里面ConcurrentHashMap锁的粒度,是数组中的某一个节点,而在jdk1.7里面。它锁定的是Segment,锁的范围要更大,所以性能上它会更低。
  2. 引入红黑树这样一个机制,去降低了数据查询的时间复杂度,红黑树的时间复杂度实是O(logn)

3.jpg如图,当数组长度不够的时候,ConcurrentHashMap它需要对数组进行扩容,而在扩容时间上,ConcurrentHashMap引入了多线程并发扩容的一个实现,简单来说多个线程对原始数组进行分片,分片之后,每个线程去负责一个分片的数据迁移,从而去整体的提升了扩容过程中的数据迁移的一个效率

  1. ConcurrentHashMap它有一个size()方法来获取总的元素个数,而在多线程并发场景中,在保证原子性的前提下去实现元素个数的累加,性能是非常低的,所以ConcurrentHashMap这个方面做了两个优化
  • (1) 4.jpg

如图,当线程竞争不激烈的时候,直接采用CAS的方式,来实现元素个数的一个递增

  • (2) 如果线程竞争化比较激烈的情况下,使用一个数组来维护元素个数,如果要增加总的元素个数的时候,直接从数组中随机选择一个,在通过CAS算法来实现原子递增,它的核心思想是引入数组来实现并发更新的一个负载

猜你喜欢

转载自juejin.im/post/7088149470956748836