HashMap与ConcurrentHashMap的一些总结

HashMap底层数据结构

JDK7:数组+链表

JDK8:数组+链表+红黑树

JDK8中的HashMap什么时候将链表转为红黑树?

当发现链表中的元素大于8之后,判断当前数组长度,如果数组长度小于64并不会马上转为红黑树,而是进行扩容。因为如果数组长度还比较小,就先利用扩容来缩小链表的长度。只有当链表中元素个数大于8,且数组长度大于64时才会将链表转为红黑树。

JDK8中的HashMap为什么要使用红黑树?

HashMap中,当元素的个数小于8时,链表的插入查询效率高于红黑树;当元素的个数大于8时,链表的插入查询效率小于红黑树。

JDK8中的HashMap的put方法的实现过程

  • 根据key生成hashcode
  • 判断当前HashMap对象中的数组是否为空,如果是空则对数组进行初始化
  • 根据逻辑与运算,算出hashcode基于当前数组对应的数组下标i
  • 判断数组中第i个位置的元素(以下称为tab[i])是否为空
    • 如果是空的,则将key-value值封装为Node对象赋值给 tab[i]
    • 如果不是空的,判断put传入的key是否等于 tab[i].key
      • 如果等于那么说明存在相同key
      • 如果不等,判断tab[i]的类型:
        • tab[i]的类型为TreeNode,表示第i个位置上的是红黑树,那么就将key-value值插入到红黑树中,并且在插入之前判断红黑树中是否存在相同key
        • tab[i]的类型不是TreeNode,表示第i个位置上的是链表,那么遍历链表查看是否有相同key,并在遍历过程中对链表的节点数进行计数,当遍历到最后一个节点时将key-value封装为Node插入链表尾部,同时判断插入新节点之前的链表节点个数是否大于8,如果是,则将链表改为红黑树
    • 如果上述步骤发现存在相同key,则根据onlyIfAbsent标记来判断是否需要更新value值,然后返回oldValue
  • modCount++
  • HashMap的元素个数size加1
  • 如果size大于扩容的阈值, 则进行扩容

JDK8中的HashMap的get方法实现过程

  • 根据key生成hashcode
  • 如果数组为空,则直接返回空
  • 如果数组不为空,则利用hashcode和数组长度通过逻辑与操作算出key所对应的数组下标i
  • 如果数组的第i个位置上没有元素,则直接返回空
  • 如果数组的第1个位上的元素的key等于get方法所传进来的key, 则返回该元素,并获取该元素的value
  • 如果不等于则判断该元素还有没有下一个元素,如果没有,返回空
  • 如果有则判断该元素的类型是链表结点还是红黑树结点
  • 如果是链表则遍历链表
  • 如果是红黑树则遍历红黑树
  • 找到即返回元素,没找到的则返回空

JDK7和JDK8中的HashMap的不同点

  • 8中使用了红黑树
  • 7中的链表使用的是头插法,8中链表使用的是尾插法
  • 7中的Hash算法比比8中的更复杂。Hash算法更复杂生成的hashcode更散列,查询性能更好,但是性能越低。8中因为使用了红黑树使性能得到了保障所以简化了Hash算法。
  • 扩容过程中7有可能对key重新进行哈希,但是8没有这个逻辑
  • 8的扩容条件和7的扩容条件不同
  • 8中多了一个APIputIfAbsent(key ,value)
  • 7和8的扩容过程中转移元素的逻辑不同

JDK7中的ConcurrentHashMap是怎么保证并发安全

主要利用Unsafe操作 + ReentrantLock+分段思想。分段数越高则支持的最大并发量越高。

ConcurrentHashMap的内部类Segment就是用来表示某一个段的。

每个Segment就是一个小型的HashMap的, 当调用ConcurrentHashMapput方法是,最终会调用到Segmentput方法,而Segment类继承了ReentrantLock,所以Segment自带可重入锁,当调用到Segmentput方法时,会先利用可重入锁加锁, 加锁成功后再将待插入的key,value插入到小型HashMap中,插入完成后解锁。

JDK7中的ConcurrentHashMap的底层原理

ConcurrentHashMap底层是由两层嵌套数组来实现的:

  • ConcurrentHashMap对象中有一个属性segments, 类型为Segment[];
  • Segment对象中有一个属性table,类型为HashEntry[];当调用ConcurrentHashMapput方法时,先根据key计算出对应的Segment[]的数组下标j,确定好当前key,value应该插入到哪个Segment对象中,如果segments[j]为空,则利用自旋锁的方式在j位置生成一个 Segment对象。然后调用Segment对象的put方法。Segment对象的put方法会先加锁, 然后也根据key计算出对应的HashEntry[]的数组下标i,然后将 key,value封装为HashEntry对象放入该位置,此过程和JDK7HashMapput方法一样,然后解锁。在加锁的过程中逻辑比较复杂,先通过自旋加锁,如果超过一 定次数就会直接阻塞等等加锁。

JDK8中的ConcurrentHashMap是怎么保证并发安全

主要利用Unsafe操作 + synchronized关键字。

Unsafe操作的使用仍然和JDK7中的类似,主要负责并发安全的修改对象的属性或数组某个位置的值。

synchronized主要负责在需要操作某个位置时进行加锁(该位置不为空),比如向某个位置的链表进行插入结点,向某个位置的红黑树插入结点。JDK8中其实仍然有分段锁的思想,只不过JDK7中段数是可以控制的,而JDK8中是数组的每一个位置都有一把锁。

当向ConcurrentHashMapput一个key,value时:

  • 首先根据key计算对应的数组下标i,如果该位置没有元素,则通过自旋的方法去向该位置赋值。
  • 如果该位置有元素,则synchronized会加锁
  • 加锁成功之后, 在判断该元素的类型:
    • 如果是链表节点则进行添加节点到链表中
    • 如果是红黑树则添加节点到红黑树
  • 添加成功后,判断是否需要进行树化
  • addCount,这个方法的意思是ConcurrentHashMap的元素个数加1,但是这个操作也是需要并发安全的,并且元素个数加1成功后,会继续判断是否要进行扩容,如果需要,则会进行扩容,所以这个方法很重要。
  • 同时一个线程在put时如果发现当前ConcurrentHashMap正在进行扩容则会去帮助扩容。

JDK7和JDK8中的ConcurrentHashMap的不同点

  • 首先是包括了HashMap中的不同点
  • JDK8中没有分段锁了, 而是使用synchronized来进行控制
  • JDK8中的扩容性能更高, 支持多线程同时扩容, 实际上JDK7中也支持多线程扩容,因为JDK7中的扩容是针对每个Segment的,所以也可能多线程扩容, 但是性能没有JDK8高, 因为JDK8中对于任意一个线程都可以去帮助扩容
  • JDK8中的元素个数统计的实现也不一 样了,JDK8中增加了CounterCell来帮助计数,而JDK7中没有,JDK7中是put的时候每个Segment内部计数,统计的时候是遍历每个Segment对象加锁统计。

[Java]HashMap与ConcurrentHashMap的一些总结的更多相关文章

  1. 轻松理解 Java HashMap 和 ConcurrentHashMap

    前言 Map 这样的 Key Value 在软件开发中是非常经典的结构,常用于在内存中存放数据. 本篇主要想讨论 ConcurrentHashMap 这样一个并发容器,在正式开始之前我觉得有必要谈谈 ...

  2. JAVA HashMap与ConcurrentHashMap

    HashMap Fast-Fail(遍历时写入操作异常) 在使用迭代器的过程中如果HashMap被修改,那么ConcurrentModificationException将被抛出,也即Fast-fai ...

  3. [Java集合] 彻底搞懂HashMap,HashTable,ConcurrentHashMap之关联.

    注: 今天看到的一篇讲hashMap,hashTable,concurrentHashMap很透彻的一篇文章, 感谢原作者的分享. 原文地址: http://blog.csdn.net/zhanger ...

  4. java基础知识再学习--HashMap与ConcurrentHashMap的区别

    引用:http://blog.csdn.net/xuefeng0707/article/details/40834595 从JDK1.2起,就有了HashMap,正如前一篇文章所说,HashMap不是 ...

  5. Java中关于Map的使用(HashMap、ConcurrentHashMap)

    在日常开发中Map可能是Java集合框架中最常用的一个类了,当我们常规使用HashMap时可能会经常看到以下这种代码: Map<Integer, String> hashMap = new ...

  6. 沉淀再出发:java中的HashMap、ConcurrentHashMap和Hashtable的认识

    沉淀再出发:java中的HashMap.ConcurrentHashMap和Hashtable的认识 一.前言 很多知识在学习或者使用了之后总是会忘记的,但是如果把这些只是背后的原理理解了,并且记忆下 ...

  7. Java并发指南13:Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析

    Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析 转自https://www.javadoop.com/post/hashmap#toc7 部分内容转自 http: ...

  8. java并发编程笔记(十)——HashMap与ConcurrentHashMap

    java并发编程笔记(十)--HashMap与ConcurrentHashMap HashMap参数 有两个参数影响他的性能 初始容量(默认为16) 加载因子(默认是0.75) HashMap寻址方式 ...

  9. Java中HashMap与ConcurrentHashMap的区别

    从JDK1.2起,就有了HashMap,正如前一篇文章所说,HashMap不是线程安全的,因此多线程操作时需要格外小心. 在JDK1.5中,伟大的Doug Lea给我们带来了concurrent包,从 ...

  10. JAVA学习:HashMap 和 ConcurrentHashMap

     一.最基本的HashMap 和 ConcurrentHashMap 1.HashMap的结构和底层原理:由数组和链表组成,数组里面每个地方都存了Key-Value这样的实例,在Java7叫Entry ...

随机推荐

  1. SpringBoot AOP 记录操作日志、异常日志

    使用SpringBoot AOP 记录操作日志.异常日志 我们在做项目时经常需要对一些重要功能操作记录日志,方便以后跟踪是谁在操作此功能.在操作某些功能时也有可能会发生异常,但是每次发生异常要定位原因 ...

  2. SpringCloud Alibaba Security安全认证

    一. Security配置(auth认证中心) 代码地址 https://github.com/typ1805/blog-cloud Spring Security是一套安全框架,可以基于RBAC(基 ...

  3. freeswitch on debian docker

    概述 freeswitch是一款简单好用的VOIP开源软交换平台. 因为centos系统期限的原因,尝试在debian的docker上使用fs. 环境 docker engine:Version 24 ...

  4. linux 通过docker安装 elasticsearch-head

    本文为博主原创,未经允许不得转载: 1. 使用docker安装 elasticsearch-head #拉取镜像 docker pull mobz/elasticsearch-head:5 #创建容器 ...

  5. 基于Html+腾讯云播SDK开发的m3u8播放器

    周末业余时间在家无事,学习了一下腾讯的云播放sdk,并制作了一个小demo(m3u8播放器),该在线工具是基于腾讯的云播sdk开发的,云播sdk非常牛,可以支持多种播放格式. 预览地址 m3u8pla ...

  6. [转帖]SMEMBERS:获取集合包含的所有元素

    https://www.bookstack.cn/read/redisguide/spilt.4.291fab46a3b4f05c.md SMEMBERS set 以下代码展示了如何使用 SMEMBE ...

  7. 使用Grafana 监控 minio 的部分改进

    使用Grafana 监控 minio 的部分改进 部署minio开启监控metrics的脚本 mkdir -p /data/minio/data cat << EOF > /etc/ ...

  8. [转帖]iozone - 性能压力测试工具

    <存储工具系列文章>主要介绍存储相关的测试和调试工具,包括不限于dd.fio.vdbench.iozone.iometer.cosbench等性能负载工具,及strace等调试工具. 1 ...

  9. [转帖]无需 zookeeper 安装 kafka 集群 (kakfa3.0 版本)

    https://xie.infoq.cn/article/7769ef4576a165f7bdf142aa3 一.kafka 集群实例角色规划 在 kafka3.0 中已经可以将 zookeeper ...

  10. [转帖]从CPU指令集自主到信息技术产业自主

    https://zhuanlan.zhihu.com/p/365210753 现代信息技术的应用都是以计算机为基础,CPU是计算机中的信息处理中枢.CPU指令集是CPU逻辑电路与操作系统和应用程序交流 ...