ConcurrentHashMap底层实现原理
- ConcurrentHashMap的整体架构
- ConcurrentHashMap的基本功能
- ConcurrentHashMap在性能方面的优化
ConcurrentHashMap的整体架构
1.8中的存储结构:
它是由数组、单向链表、红黑树组成的。
当们去初始化一个ConcurrentHashMap实例的时候,默认会初始化一个长度等于16的数组。
由于ConcurrentHashMap的核心仍然是Hash表,所以必然会存在hash冲突的问题。所以ConcurrentHashMap采用链式寻址的方式来解决Hash表的冲突。
当Hash冲突比较多的时候,会造成链表长度较长的问题,所以这种情况下会使得ConcurrentHashMap中的一个数组元素的查询复杂度会增加,所以在JDK1.8里面,引入了红黑树机制。
当数组长度大于64并且链表的长度大于等于8的时候,单向链表就会转化成红黑树。另外随着ConcurrentHashMap的一个动态扩容,一旦链表的长度小于8,红黑树会退化成单项链表。
ConcurrentHashMap的基本功能
本质上是一个HashMap,因此功能和HashMap是一样的。但是ConcurrentHashMap在HashMap的基础上提供了并发安全的一个实现。
并发安全的主要实现是通过对于Node节点去加锁来保证数据更新的安全性。
ConcurrentHashMap在性能方面的优化
如何在并发性能和数据安全性之间去做好平衡:
在很多地方都有类似的设计,比如说像cpu的三级缓存、mysql的buffer_pool、Synchronized的锁升级等等。
ConcurrentHashMap也做了类似的一个优化,主要体现在几个方面:
- 在JDK1.8里面,ConcurrentHashMap它的锁的粒度是数组中的某一个节点。而在JDK1.7里面,它锁定的是Segment,锁的范围要更大,所以性能上它会更低。
- 引入红黑树这样一个机制,去降低了数据查询的时间复杂度。红黑树的时间复杂度是O(logn)。
- 当数组的长度不够的时候,ConcurrentHashMap它需要对数组进行扩容,而在扩容的时间上,ConcurrentHashMap引入了多线程并发扩容的一个实现。简单来说,就是多个线程对原始数组进行分片,分片之后,每个线程去负责一个分片的数据迁移。从而去整体地提升了扩容过程中的数据迁移的一个效率。
- ConcurrentHashMap它有一个size()方法来获取总的元素个数,而在多线程并发场景中,在保证原子性的前提下去实现元素个数的累加。性能是非常低的。所以ConcurrentHashMap在这个方面做了两个点的优化:
当线程竞争不激烈的时候,直接采用CAS的方式来实现元素个数的一个原子递增。如果线程竞争比较激烈的情况下,使用一个数组来维护元素的个数。如果要增加总的元素个数的时候,直接从数组中随机选择一个再通过CAS算法来实现原子递增。它的核心思想是引入了数组来实现对并发更新的一个负载。
参考资料:【Java面试】面试被问:ConcurrentHashMap 底层实现原理?