ConcurrentHahMap 基于源码解析

目录

1、简单总结

2、JDK1.7 segments + HashEntry数组 + 链表解析

3、JDK1.8 散列表 + 红黑树 + CAS 解析


1、简单总结

  • JDK1.8底层是散列表+红黑树、JDK1.7底层是segments + HashEntry数组;
  • ConCurrentHashMap支持高并发的访问和更新,它是线程安全的;
  • 检索操作不用加锁,get方法是非阻塞的;
  • key和value都不允许为null

2、JDK1.7 segments + HashEntry数组 + 链表解析

参考:https://zhuanlan.zhihu.com/p/49754357

如下图所示,segments继承了可重入锁ReentrantLock,每个片段有一个锁,叫做“分段锁”。

1).Segment(分段锁)

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

2).内部结构

ConcurrentHashMap使用分段锁技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。如下图是ConcurrentHashMap的内部结构图:

 

 

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

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

3).该结构的优劣势

坏处

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

好处

写操作的时候可以只对元素所在的Segment进行加锁即可,不会影响到其他的Segment,这样,在最理想的情况下,ConcurrentHashMap可以最高同时支持Segment数量大小的写操作(刚好这些写操作都非常平均地分布在所有的Segment上)。

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


3、JDK1.8 散列表 + 红黑树 + CAS 解析

参考:https://zhuanlan.zhihu.com/p/35668936

JDK8中ConcurrentHashMap参考了JDK8 HashMap的实现,采用了数组+链表+红黑树的实现方式来设计,内部大量采用CAS操作。

CAS:是compare and swap的缩写,即我们所说的比较交换。CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。CAS是通过无限循环来获取数据的,若果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。

Node:JDK8中彻底放弃了Segment转而采用的是Node(保存key,value及key的hash值的数据结构。其中value和next都用volatile修饰,保证并发的可见性。),其设计思想也不再是JDK1.7中的分段锁思想。

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

红黑树:在JDK8中ConcurrentHashMap的结构,由于引入了红黑树,使得ConcurrentHashMap的实现非常复杂,我们都知道,红黑树是一种性能非常好的二叉查找树,其查找性能为O(logN),但是其实现过程也非常复杂,而且可读性也非常差。

总结:

JDK1.8版本中synchronized+CAS+HashEntry+红黑树

  • 数据结构:取消了Segment分段锁的数据结构,取而代之的是数组+链表+红黑树的结构。
  • 保证线程安全机制:JDK1.7采用segment的分段锁机制实现线程安全,其中segment继承自ReentrantLock。JDK1.8采用CAS+Synchronized保证线程安全。
  • 锁的粒度:原来是对需要进行数据操作的Segment加锁,现调整为对每个数组元素加锁(Node)。
  • 链表转化为红黑树:定位结点的hash算法简化会带来弊端,Hash冲突加剧,因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。
  • 查询时间复杂度:从原来的遍历链表O(n),变成遍历红黑树O(logN)。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ConcurrentHashMap 是 Java 并发包中提供的一个线程安全的哈希表实现,它的扩容机制主要有以下几个过程: 1. ConcurrentHashMap 内部维护了一个 Segment 数组,每个 Segment 是一个独立的、线程安全的哈希表,初始情况下,ConcurrentHashMap 的容量为 16,Segment 数组的长度也为 16。 2. 当 ConcurrentHashMap 中的元素个数超过阈值时,就需要进行扩容操作。ConcurrentHashMap 扩容时,并不会像普通的哈希表那样对整个表进行重构,而是只对需要扩容的 Segment 进行重构。 3. ConcurrentHashMap 扩容时,会先将需要进行扩容的 Segment 进行标记,然后创建一个新的 Segment 数组,长度为原来的两倍,并将原来的元素重新分配到新的 Segment 数组中。这个过程是并发进行的,每个线程负责处理一部分元素。 4. 在元素分配的过程中,如果多个线程同时对同一个 Segment 进行插入或者删除操作,就需要使用来保证线程安全。 5. 当所有的元素都分配完成后,就可以将原来的 Segment 数组替换为新的 Segment 数组,扩容操作完成。 需要注意的是,ConcurrentHashMap 在扩容时并不会阻塞所有的操作,而是只会阻塞需要进行扩容的 Segment 相关的操作,因此它可以在高并发的情况下保持较好的性能。同时,ConcurrentHashMap 采用分段的方来保证线程安全,可以支持多个线程同时进行读操作,提高了并发性能。 ### 回答2: ConcurrentHashMap是一个线程安全的哈希表实现,在多线程环境下提供高效的读写操作。在并发情况下,它采用了分段的策略来提高并发性能。 ConcurrentHashMap的扩容机制是为了保持加载因子不超过某个限定值,以避免哈希冲突的增加和哈希表的过度填装。当哈希表的负载因子超过阈值时,就会触发扩容操作。 扩容操作包括以下几个步骤: 1. 首先,会将旧的哈希表分成多个段,每个段都有自己的,以便并发访问不同的段。每个段中包含一个或多个哈希桶,每个哈希桶包含若干个键值对。 2. 然后,会创建一个新的哈希表,其大小是旧哈希表的两倍。新哈希表的每个段都会有一个与之对应的旧哈希表段,并且会复制旧哈希表段中的键值对到新哈希表段中。 3. 在复制过程中,会使用读写来控制并发访问。读用于读取旧哈希表段中的键值对,写用于更新新哈希表段中的键值对。 4. 复制完成后,更新旧哈希表段的引用,使其指向新哈希表段,从而实现无缝切换。 通过这样的扩容机制,ConcurrentHashMap实现了高效的并发操作。当多个线程同时对不同段进行操作时,不会发生争用,从而提高了并发性能。同时,通过分段和读写的控制,保证了线程安全性,避免了数据不一致的问题。 总的来说,ConcurrentHashMap的扩容机制是为了提高并发性能和保证线程安全性。通过分段和读写的组合方,在多线程环境下实现高效的读写操作。 ### 回答3: ConcurrentHashMap是Java中线程安全的HashMap实现,它通过使用一种分段Segment)的方来实现高并发的读写操作。在多线程环境下,ConcurrentHashMap的扩容机制是为了提高并发度和容量的动态调整。 ConcurrentHashMap在创建时会预先分配一定数量的Segment(一般为16个),每个Segment都是一个独立的,可以并发地进行读写操作。当一个线程需要写入数据时,只需要获取对应Segment,而不会影响到其他线程读写其他Segment的操作。 当ConcurrentHashMap中的某个Segment的负载因子(load factor)超过阈值时(默认为0.75),会触发对该Segment的扩容。扩容过程中,会将该Segment中的所有数据进行重新哈希(rehash),同时增加新的Segment。 扩容过程中,对于正在进行写操作的线程,会进行重试,因为这些线程可能在扩容开始时获取了旧Segment,但在重新哈希的过程中,新的Segment可能会被新的线程访问到。同时,在扩容阶段进行读操作的线程可以访问旧Segment或新Segment,不会对并发读操作造成阻塞。 在扩容过程中,为了避免影响并发读写操作的性能,ConcurrentHashMap会将原有的Segment数组复制一份,并对每个Segment进行重新哈希。此步骤可能引起短暂的性能下降,但可以有效避免扩容对并发读写操作的影响。 总之,ConcurrentHashMap的扩容机制通过分段和重新哈希的方,实现了高并发环境下的数据安全性和扩容能力。这使得ConcurrentHashMap成为处理并发读写的优秀工具,适用于高并发环境下的数据存储和访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值