题外话:为什么要hashcode进行spread? 充分使用key.hashCode()的高16位信息,保证hash分布更分散,

扩容操作是新建2倍于原表大小的新表,并将原表结点拷贝一份放在新表中,对原表无修改或修改很小。当原表所有结点都已被拷贝到新表中后,原表会被垃圾回收。

在jdk7中的HashMap实现类中,数组+链表。扩容操作是将原数组的结点一一进行hash计算,然后一一挂接到新数组上,所以不是基于复制结点的机制。
在jdk7中的ConcurrentHashMap实现类中,段(segment)+数组+链表。扩容操作是先遍历数组元素,在每个数组元素上遍历一遍链表,找到链表的最后n个结点(这n个结点在新数组一定属于同一个数组位置上),把这n个结点先挂接到新数组的数组位置上,这也叫lastRun机制。至于原数组的头结点到倒数n个结点之间的结点,再遍历一遍,通过复制每个结点的机制挂接到新数组上。

jdk8中的HashMap实现类中,数组+链表/红黑树。扩容操作是将原数组每个结点的hash值和原数组长度进行“与”操作,结果等于0代表该结点位置不变,落在新数组的同样位置,否则该结点在新数组的位置是[j + oldCap]上。
原因是:扩容是将数组长度扩大一倍,假如原长度是16(二进制是10000,掩码1111),新长度是32(二进制是100000,掩码11111),那么在计算结点所落的位置时,hash值原本是低4位参与计算,扩容后变成hash值低5位参与计算,这样的话,当参与运算的最高位也就是第五位,是1时,必然落在扩容后的新的位置,是0时,必然位置不变,因为原数组长度16的二进制第五位是1,所以通过将结点的hash值和原数组长度进行与操作,就可以知道结点在新数组中是保持相对不变还是落在高一点的位置上。

jdk8中的ConcurrentHashMap实现类中,数组+链表/红黑树。扩容操作是多个线程参与共同完成的,相比于jdk7版本的扩容,jdk8的扩容属于渐进式扩容,不是一蹴而就。将原数组长度为n作为扩容任务的总数,切分成m块作为m个小任务,每个小任务有且只有一个线程来负责完成扩容(因为扩容后的数组长度是原来的2倍,结点要么在新数组的相对原位置i,要么在i+OldTableSize处,所以其他线程在扩容别的小任务时,不会和当前线程存在位置冲突)。对于扩容时,链表同样先找lastRun然后挂接到新数组上,前面的结点再通过复制的机制挂接到新数组上。

HashMap并发问题:死循环的原因
Hashmap的Resize包含扩容和ReHash两个步骤,ReHash在并发的情况下可能会形成链表环。因为,链表采用头插法,将原数组转移到新数组时,会从前向后遍历链表结点,头插法机制恰好使新数组中结点的相对顺序和原数组中颠倒过来。在并发的时候,假如原来的结点顺序被线程A颠倒了,而被挂起的线程b在恢复执行后,拿扩容前的节点和顺序继续完成第一次循环,而后又遵循A线程扩容后的链表顺序重新排列链表中的顺序,即又颠倒了一下顺序,最终形成了环。

jdk7和8中关于HashMap和concurrentHashMap的扩容过程总结,以及HashMap死循环的更多相关文章

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

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

  2. 调试JDK源代码-一步一步看HashMap怎么Hash和扩容

    调试JDK源代码-一步一步看HashMap怎么Hash和扩容 调试JDK源代码-ConcurrentHashMap实现原理 调试JDK源代码-HashSet实现原理 调试JDK源代码-调试JDK源代码 ...

  3. HashMap 什么时候进行扩容呢

    HashMap扩容: 当HashMap中的元素越来越多的时候,碰撞的几率也就越来越高(因为数组的长度是固定的),所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在A ...

  4. Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析

    Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析 今天发一篇”水文”,可能很多读者都会表示不理解,不过我想把它作为并发序列文章中不可缺少的一块来介绍.本来以为花不了 ...

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

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

  6. Java7/8 中 HashMap 和 ConcurrentHashMap的对比和分析

    大家可能平时用HashMap比较多,相对于ConcurrentHashMap 来说并不是很熟悉.ConcurrentHashMap 是 JDK 1.5 添加的新集合,用来保证线程安全性,提升 Map ...

  7. JDK7与JDK8中HashMap的实现

    JDK7中的HashMap HashMap底层维护一个数组,数组中的每一项都是一个Entry transient Entry<K,V>[] table; 我们向 HashMap 中所放置的 ...

  8. java7,java8 中HashMap和ConcurrentHashMap简介

    一:Java7 中的HashMap 结构: HashMap 里面是一个数组,然后数组中每个元素是一个单向链表.链表中每个元素称为一个Entry 实例,Entry 包含四个属性:key, value, ...

  9. Java7与Java8中的HashMap和ConcurrentHashMap知识点总结

    JAVA7 Java7的ConcurrentHashMap里有多把锁,每一把锁用于其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率呢.这 ...

随机推荐

  1. HDU1754-ZKW线段树

    单点更新,区间最值 HDU 1754 // // Created by helica on 2018/3/18. // //zkw线段树 单点修改 区间求最值 //HDU 1754 #include ...

  2. OrCAD原理图中怎么导出FPGA的引脚分配

    流程 (1)选择tool下的export FPGA: (2)选择厂商,选择器件型号.选择生成文件类型. 以上.

  3. pytest 15 fixture之autouse=True

    前言 平常写自动化用例会写一些前置的fixture操作,用例需要用到就直接传该函数的参数名称就行了.当用例很多的时候,每次都传这个参数,会比较麻烦.fixture里面有个参数autouse,默认是Fa ...

  4. DotNet进阶系列

    一. 回顾历史 回顾个人发展历程,自2012年初次接触开发至今(2018年)已经有六个年头,这期间陆陆续续学习并掌握了不少技术,C#语言.ORM框架.多线程技术.设计模式.前端技术.MVC.MVVM框 ...

  5. MySQL8.0

    序言 my.ini [mysqld] # 设置3306端口 port=3306 # 设置mysql的安装目录 basedir=D:\\DataBase\\mysql-8.0.12-winx64 # 设 ...

  6. mysql 关联表修改数据

    UPDATE t1 INNER JOIN t2 ON t1.c1=t2.c1  SET t1.c2=value WHERE t1`removed`=0 AND t2`removed`=0 AND t1 ...

  7. 将Python3导出为exe程序

    一.pyinstaller简介 Python是一个脚本语言,被解释器解释执行.它的发布方式: .py文件:对于开源项目或者源码没那么重要的,直接提供源码,需要使用者自行安装Python并且安装依赖的各 ...

  8. Activiti工作流学习笔记

    先从工作流的启动开始讲,Activiti提供了四种工作流的启动方式 1.空启动事件 2.定时启动事件 3.异常启动事件 4.消息启动事件 空启动事件中标签内没有任何其他元素的定义 <startE ...

  9. ASP.NET Web API 2 OData v4教程

    程序数据库格式标准化的开源数据协议 为了增强各种网页应用程序之间的数据兼容性,微软公司启动了一项旨在推广网页程序数据库格式标准化的开源数据协议(OData)计划,于此同时,他们还发 布了一款适用于OD ...

  10. 请求数据loading

    请求数据加载,CSS3实现 HTML: <!--请求数据loading--> <div class="back_loading"> <div clas ...