ConcurrentHashMap

Posted dingpeng9055

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ConcurrentHashMap相关的知识,希望对你有一定的参考价值。

JDK1.5开始加入了ConcurrentHashMap

 一个ConcurrentHashMap实例中包含由若干个Segment实例组成的数组,而一个Segment实例又包含由若干个桶,每个桶中都包含一条由若干个 HashEntry 对象链接起来的链表。特别地,ConcurrentHashMap 在默认并发级别下会创建16个Segment对象的数组,如果键能均匀散列,每个 Segment 大约守护整个散列表中桶总数的 1/16

(通俗:内存直接分为了16个segment,每个segment实际上还是存储的哈希表,写入的时候,先找到对应的segment,然后锁这个segment,写完,解锁,)

 ConcurrentHashMap具体是怎么实现线程安全的呢,肯定不可能是每个方法加synchronized,那样就变成了HashTable。

从ConcurrentHashMap代码中可以看出,它引入了一个“分段锁”的概念,具体可以理解为把一个大的Map拆分成N个小的HashTable,根据key.hashCode()来决定把key放到哪个HashTable中。

在ConcurrentHashMap中,就是把Map分成了N个Segment,put和get的时候,都是现根据key.hashCode()算出放到哪个Segment中:

ConcurrentHashMap就是一个分段的hashtable ,根据自定的hashcode算法生成的对象来获取对应hashcode的分段块进行加锁,不用整体加锁,提高了效率

 技术图片

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

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

缺点

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

优点

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

 

concurrentHashMap 读加锁吗?读是怎么实现不加锁的

 

在jdk1.7中是采用Segment + HashEntry + ReentrantLock的方式进行实现的,

1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现。

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

 Node:保存key,value及key的hash值的数据结构。其中value和next都用volatile修饰,保证并发的可见性。

 

1.8中ConcurrentHashMap的get操作全程不需要加锁,这也是它比其他并发集合如hashtable、用Collections.synchronizedMap()包装的hashmap;安全效率高的原因之一。

get操作全程不需要加锁是因为Node的成员val是用volatile修饰的和数组用volatile修饰没有关系。

数组用volatile修饰主要是保证在数组扩容的时候保证可见性。

其实可以看出JDK1.8版本的ConcurrentHashMap的数据结构已经接近HashMap,相对而言,ConcurrentHashMap只是增加了同步的操作来控制并发,从JDK1.7版本的ReentrantLock+Segment+HashEntry,到JDK1.8版本中synchronized+CAS+HashEntry+红黑树。

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

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

3.锁的粒度:原来是对需要进行数据操作的Segment加锁,现调整为对每个数组元素加锁(Node)。

4.链表转化为红黑树:定位结点的hash算法简化会带来弊端,Hash冲突加剧,因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。

5.查询时间复杂度:从原来的遍历链表O(n),变成遍历红黑树O(logN)。

 

https://mp.weixin.qq.com/s/vUDnd5JZXZV9J35CoT9AMw

https://www.jianshu.com/p/e694f1e868ec?from=timeline

https://baijiahao.baidu.com/s?id=1617089947709260129&wfr=spider&for=pc

 

以上是关于ConcurrentHashMap的主要内容,如果未能解决你的问题,请参考以下文章