HashMap与ConcurrentHashMap

Posted zhangbochao

tags:

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

  • HashMap(1.7)

  HashMap是一个用于存储Key-Value键值对的集合,其内部结构为链表数组。每一个键值对也叫做Entry。这些个键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干。put时,思想是通过key的哈希值,模HashMap的初始值,决定插入位置,当出现哈希冲突时,采用头插法。(HashMap的发明者认为,后插入的Entry被查找的可能性更大。故采用头插法。其次,插到最后的话,复杂度为O(n))但实际上,并非进行了求模运算,而是位运算。此处要求哈希长度默认值必须为2的N次方,用key的哈希值&size-1)的方式代替求模。只有当长度为2的n次方时(默认长度为16)时,长度减1的二进制都为000...11111,key的哈希值&size-1)才可与求模运算值相同。

  • HashMap(1.8)

  引入红黑树,当某一链表长度到达阈值,将链表转换为红黑树。Node 的核心组成其实也是和 1.7 中的 HashEntry 一样,存放的都是 key value hashcode next 等数据。

  HashEntry 修改为 Node。

  • ConcurrentHashMap(1.7)

  ConcurrentHashMap考虑到并发性能,它没有像Hashtable一样简单的给每个公有方法加上synchronize,而是利用了JUC包中提供的多种并发特性,在尽量保持性能的前提下实现了多线程安全。它采用多Segment(段)的结构来增加了并发性。

ConcurrentHashMap的并发思路就是将Map分为多个Segment,每个Segment含有一个类似HashMap中的hash表,每个hash表中的元素都是一个键值对Entry,此Entry可以组成一个单向链表。简单的说,ConcurrentHashMap是由多个hash表构成的双重hash表。

  当要插入数值时,分为三步:第一步计算key的hash值;第二步用此hash值的前n位(2的n次方即为segment的长度,注意segment的长度永远是2的n次方)截取后作为segment的索引;第三步找到该segment的hash表,用(tab.length-1)&hash作为hash表的索引,然后在此位置插入数值。

  查询数据时亦然,首先计算key的hash值,然后找到对应的segment,然后从segment的hash表中查到链表,再依次在链表中搜索相应的Entry。

  Segment继承了ReentrantLock,因此它实际上是一把锁。在进行put、remove、replace、clear等需要改动内部内容的操作时,都要进行加锁操作。采用自旋锁和独占锁结合的方法,在很多场景下能够提高Segment并发操作数据的效率。

  ConcurrentHashMap的get操作不需要加锁!因为volatile字段写先于读。size方法也不需要加锁,在无所状态下,多次通过遍历获得sum值,若连续两次sum值相同,则认为此时的sum就位size。

  • ConcurrentHashMap(1.8)

  抛弃了原有的 Segment 分段锁,而采用了 CAS + synchronized 来保证并发安全性。

 

 

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

HashMap与ConcurrentHashMap

HashMap与ConcurrentHashMap的区别

HashMap与ConcurrentHashMap的区别

java集合之HashMap与ConcurrentHashMap的自我理解

ConcurrentHashMap以及HashMap,HashTable的区别

HashMap与ConcurrentHashMap