jdk7和8中关于HashMap和concurrentHashMap的扩容过程总结,以及HashMap死循环
题外话:为什么要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死循环的更多相关文章
- 沉淀再出发:java中的HashMap、ConcurrentHashMap和Hashtable的认识
沉淀再出发:java中的HashMap.ConcurrentHashMap和Hashtable的认识 一.前言 很多知识在学习或者使用了之后总是会忘记的,但是如果把这些只是背后的原理理解了,并且记忆下 ...
- 调试JDK源代码-一步一步看HashMap怎么Hash和扩容
调试JDK源代码-一步一步看HashMap怎么Hash和扩容 调试JDK源代码-ConcurrentHashMap实现原理 调试JDK源代码-HashSet实现原理 调试JDK源代码-调试JDK源代码 ...
- HashMap 什么时候进行扩容呢
HashMap扩容: 当HashMap中的元素越来越多的时候,碰撞的几率也就越来越高(因为数组的长度是固定的),所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在A ...
- Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析
Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析 今天发一篇”水文”,可能很多读者都会表示不理解,不过我想把它作为并发序列文章中不可缺少的一块来介绍.本来以为花不了 ...
- Java中关于Map的使用(HashMap、ConcurrentHashMap)
在日常开发中Map可能是Java集合框架中最常用的一个类了,当我们常规使用HashMap时可能会经常看到以下这种代码: Map<Integer, String> hashMap = new ...
- Java7/8 中 HashMap 和 ConcurrentHashMap的对比和分析
大家可能平时用HashMap比较多,相对于ConcurrentHashMap 来说并不是很熟悉.ConcurrentHashMap 是 JDK 1.5 添加的新集合,用来保证线程安全性,提升 Map ...
- JDK7与JDK8中HashMap的实现
JDK7中的HashMap HashMap底层维护一个数组,数组中的每一项都是一个Entry transient Entry<K,V>[] table; 我们向 HashMap 中所放置的 ...
- java7,java8 中HashMap和ConcurrentHashMap简介
一:Java7 中的HashMap 结构: HashMap 里面是一个数组,然后数组中每个元素是一个单向链表.链表中每个元素称为一个Entry 实例,Entry 包含四个属性:key, value, ...
- Java7与Java8中的HashMap和ConcurrentHashMap知识点总结
JAVA7 Java7的ConcurrentHashMap里有多把锁,每一把锁用于其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率呢.这 ...
随机推荐
- Golang 介绍与安装
1.介绍与安装 Golang 是什么 Go 亦称为 Golang(按照 Rob Pike 说法,语言叫做 Go,Golang 只是官方网站的网址),是由谷歌开发的一个开源的编译型的静态语言. Gola ...
- JumpServer1.0 服务搭建
JumpServer1.0 服务搭建 系统环境配置 setenforce 0 systemctl stop iptables.service systemctl stop firewalld.serv ...
- CMakeList.txt(2):CMakeLists.txt编写规则
#project namePROJECT(test_math) 指定生成的工程名为test_math #head file path INCLUDE_DIRECTORIES(includ ...
- 关于vue的增删改查操作
利用vue也可以实现数据的增删改查,只是未涉及到数据库,只是在浏览器页面中进行操作. 将datas数组中的数据循环输出: 再增加一行,用于保存新数据,编辑数据后保存: 此时,数据已经呈现出来,开始进行 ...
- TF用法指导
一.变量 1 .tf.Variables() 功能:创建变量 2.tf.get_variable() 功能:创建变量,注意和上面的区别 3.tf.variable_scope() 功能:其实只要记住一 ...
- java基础-容器-Set
Set:set不存重复元素,如果是使用set存储java预定义的Integer,String等类型会很简单,如果是存储自定义类型的数据类型,就必须要重新定义equals()方法以确保set中保存的对象 ...
- Docker安装步骤
在学习springcloud的消息总线时,需要安装rabbitmq,因为rabbitmq是用erlang开发的,所以安装rabbitmq又需要先安装erlang,总之安装过程中遇到各种坑,然而最终还是 ...
- HBase基本概念与基本使用
1. HBase简介 1.1 什么是HBase HBASE是一个高可靠性.高性能.面向列.可伸缩的分布式存储系统,利用HBASE技术可在廉价PC Server上搭建起大规模结构化存储集群. HBASE ...
- Future of Future
innovation 革新 , <社会创新实验室 宣传片>的个人记录(有加戏便于我自己理解) 1. 清洁能源 => sustainable 家. 2. 老龄化 => 外出接 ...
- Linux基础系统优化及常用命令
# Linux基础系统优化及常用命令 [TOC] ## Linux基础系统优化 Linux的网络功能相当强悍,一时之间我们无法了解所有的网络命令,在配置服务器基础环境时,先了解下网络参数设定命令. - ...