ConcurrentHashMap底层探索

目录

整体架构

JDK1.7

 JDK1.8

JDK1.7与JDK1.8的优化区别

扩容机制 

如何保证安全性?


整体架构

JDK1.7

        ConcurrentHashMap采用的是分段锁(ReentrantLock锁)的机制。 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率,Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。具体来说,它内部(一大一小数组和一个链表的结构)维护着一个Segment数组(类似于数据库中的一张表),每个Segment都包含一个HashEntry数组(类似于数据库中的一表中数据条),这些HashEntry数组组成了整个Map的桶数组,每个桶中存放着一条链表,链表中的节点是键值对的形式。当执行写操作时,只会对相应的Segment加锁,而不是对整个Map加锁,这样可以降低锁的粒度,减少冲突。读取元素时, ConcurrentHashMap不需要加锁,各个段上的链表可以并发访问。

 JDK1.8

        ConcurrentHashMap的内部实现做出了进一步改进。由数组、单向链表、红黑树组成。它取消了分段锁的机制(改用CAS或synchronized关键字), 重新设计了内部结构(由于链表的存在,所以za遍历的效率新增红黑树来提高遍历效率)。它将内部数据结构设计为了带有多个单向链表的哈希表,每个单向链表称作一个Bucket,Bucket中共存储若干键值对。在ConcurrentHashMap的初始化阶段,首先会构造一个默认大小为16的哈希表,里面有16个Bucket。 当插入元素时,ConcurrentHashMap会将元素根据hash值散列到对应的Bucket中,然后使用synchronized关键字来保护该Bucket,覆盖旧的元素或添加新的元素。在读取元素时,ConcurrentHashMap会先通过volatile关键字判断Bucket是否被修改过,如果没有被修改过,则直接访问;否则以不加锁的方式,通过CAS算法实现线程安全地遍历Bucket

        当链表长度大于8,并且数组长度大于64的时候,链表就会转变为红黑树的结构;另外,随着ConcurrentHashMap的动态扩容,一旦链表长度小于8,红黑树会退化成单向链表。

        ConcurrentHashMap采用链式寻址法来解决hash冲突

JDK1.7与JDK1.8的优化区别

  1. 实现方式不同。在JDK 1.7中,ConcurrentHashMap默认使用分段锁机制,即将整个哈希表划分成多个独立的段,在每个段上采用ReentrantLock实现并发访问;而在JDK 1.8中,ConcurrentHashMap改为使用CAS+Synchronized实现,取消了锁粒度的划分,采用更细粒度的锁控制。

  2. 线程安全性能得到提升。JDK 1.8的ConcurrentHashMap通过增加分离器(一种调整下标分配策略的对象)和共享节点(仅有一个入口连接所有键值对)大大减少了锁争用的情况,从而达到更好的线程安全性能。

  3. 高并发解决方案更优秀。JDK 1.8的ConcurrentHashMap强化了其高并发解决方案,在处理并发冲突方面比JDK 1.7的实现更加可靠。具体来说,JDK 1.8利用Unsafe类给定的CAS操作底层原理,在保证线程安全的同时也提供了高效的并发处理能力,使其可以适应更广泛的并发场景。

总的来说,JDK 1.8的ConcurrentHashMap相较于JDK 1.7版本在性能和安全上都有所提高,并且对于高并发场景表现的更佳,但同时由于其实现方式更为复杂,增加了一定的代码难度和理解成本。


扩容机制 

         当数组长度不够时,ConcurrentHashMap需要对数组进行扩容,在扩容的实现上,ConcurrentHashMap引入了多线程并发扩容的机制,简单来说就是多个线程对原始数组进行分片后,每个线程负责一个分片的数据迁移,从而提升了扩容过程中数据迁移的效率。

ConcurrentHashMap中有一个size()方法来获取总的元素个数,而在多线程并发场景中,在保证原子性的前提下来实现元素个数的累加,性能是非常低的。ConcurrentHashMap在这个方面的优化主要体现在两个点:

当线程竞争不激烈时,直接采用CAS来实现元素个数的原子递增。

如果线程竞争激烈,使用一个数组来维护元素个数,如果要增加总的元素个数,则直接从数组中随机选择一个,再通过CAS实现原子递增。它的核心思想是引入了数组来实现对并发更新的负载


如何保证安全性?

        ConcurrentHashMap通过多个锁对象(默认为16个)将数据分成多个段,每个段都由一个锁负责,从而实现对不同段之间的并发访问。这样的设计可以增加并发性,提高性能,并且减少资源争用,避免等待所有数据结构的锁造成的连锁阻塞。

        具体来说,当读写操作需要对某一段进行访问时,只需要获取该段所对应的锁即可,而其他段则不受影响。这样,在高并发场景下,多个线程可以同时读取或者写入ConcurrentHashMap的不同段,互不干扰,从而保证了线程安全性。

        此外,ConcurrentHashMap内部采用CAS算法来控制添加和删除元素的过程,确保多个线程对同一元素的修改不会产生冲突。在进行哈希冲突处理时,ConcurrentHashMap还会判断是否存在相同的key,以避免出现覆盖或者混淆的情况

        总体来说,ConcurrentHashMap采用分段锁、CAS算法和哈希冲突处理等机制来保证线程安全性,并且其尽可能避免了锁的粒度过大的问题,进而提供了更好的性能表现。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学徒630

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值