ConcurrentHashMap相关的文章网上有很多,而关于ConcurrentHashMap扩容机制是很关键的点,尤其是在并发的情况下实现数组的扩容的问题经常会碰到,看到这篇写的具有代表性,详细讲解了ConcurrentHashMap是如何在并发情况扩容的。

转自简书:占小狼http://www.jianshu.com/u/90ab66c248e6

什么情况会触发扩容

当往hashMap中成功插入一个key/value节点时,有可能触发扩容动作:
1、如果新增节点之后,所在链表的元素个数达到了阈值 8,则会调用treeifyBin方法把链表转换成红黑树,不过在结构转换之前,会对数组长度进行判断,实现如下:

如果数组长度n小于阈值MIN_TREEIFY_CAPACITY,默认是64,则会调用tryPresize方法把数组长度扩大到原来的两倍,并触发transfer方法,重新调整节点的位置。

2、新增节点之后,会调用addCount方法记录元素个数,并检查是否需要进行扩容,当数组元素个数达到阈值时,会触发transfer方法,重新调整节点的位置。

transfer实现

transfer方法实现了在并发的情况下,高效的从原始组数往新数组中移动元素,假设扩容之前节点的分布如下,这里区分蓝色节点和红色节点,是为了后续更好的分析:

在上图中,第14个槽位插入新节点之后,链表元素个数已经达到了8,且数组长度为16,优先通过扩容来缓解链表过长的问题,实现如下:
1、根据当前数组长度n,新建一个两倍长度的数组nextTable

2、初始化ForwardingNode节点,其中保存了新数组nextTable的引用,在处理完每个槽位的节点之后当做占位节点,表示该槽位已经处理过了;

3、通过for自循环处理每个槽位中的链表元素,默认advace为真,通过CAS设置transferIndex属性值,并初始化ibound值,i指当前处理的槽位序号,bound指需要处理的槽位边界,先处理槽位15的节点;

4、在当前假设条件下,槽位15中没有节点,则通过CAS插入在第二步中初始化的ForwardingNode节点,用于告诉其它线程该槽位已经处理过了;

5、如果槽位15已经被线程A处理了,那么线程B处理到这个节点时,取到该节点的hash值应该为MOVED,值为-1,则直接跳过,继续处理下一个槽位14的节点;

6、处理槽位14的节点,是一个链表结构,先定义两个变量节点lnhn,按我的理解应该是lowNodehighNode,分别保存hash值的第X位为0和1的节点,具体实现如下:

使用fn&n可以快速把链表中的元素区分成两类,A类是hash值的第X位为0,B类是hash值的第X位为1,并通过lastRun记录最后需要处理的节点,A类和B类节点可以分散到新数组的槽位14和30中,在原数组的槽位14中,蓝色节点第X为0,红色节点第X为1,把链表拉平显示如下:

1、通过遍历链表,记录runBitlastRun,分别为1和节点6,所以设置hn为节点6,ln为null;
2、重新遍历链表,以lastRun节点为终止条件,根据第X位的值分别构造ln链表和hn链表:

ln链:和原来链表相比,顺序已经不一样了

hn链:

通过CAS把ln链表设置到新数组的i位置,hn链表设置到i+n的位置;

7、如果该槽位是红黑树结构,则构造树节点lohi,遍历红黑树中的节点,同样根据hash&n算法,把节点分为两类,分别插入到lohi为头的链表中,根据lohi链表中的元素个数分别生成lnhn节点,其中ln节点的生成逻辑如下:
(1)如果lo链表的元素个数小于等于UNTREEIFY_THRESHOLD,默认为6,则通过untreeify方法把树节点链表转化成普通节点链表;
(2)否则判断hi链表中的元素个数是否等于0:如果等于0,表示lo链表中包含了所有原始节点,则设置原始红黑树给ln,否则根据lo链表重新构造红黑树。

最后,同样的通过CAS把ln设置到新数组的i位置,hn设置到i+n位置。

ConcurrentHashMap的扩容机制(jdk1.8)的更多相关文章

  1. JDK1.8前_HashMap的扩容机制原理

    最近在研究hashmap的扩容机制,作为一个小白,相信我的理解,对于一些同样是刚刚接触hashmap的白白是有很很大的帮助,毕竟你去看一些已经对数据结构了解透彻的大神谈hashmap的原理等,人家说的 ...

  2. HashMap底层结构、原理、扩容机制

    https://www.jianshu.com/p/c1b616ff1130 http://youzhixueyuan.com/the-underlying-structure-and-princip ...

  3. 高并发编程系列:ConcurrentHashMap的实现原理(JDK1.7和JDK1.8)

    HashMap.CurrentHashMap 的实现原理基本都是BAT面试必考内容,阿里P8架构师谈:深入探讨HashMap的底层结构.原理.扩容机制深入谈过hashmap的实现原理以及在JDK 1. ...

  4. 深入理解HashMap的扩容机制

    什么时候扩容: 网上总结的会有很多,但大多都总结的不够完整或者不够准确.大多数可能值说了满足我下面条件一的情况. 扩容必须满足两个条件: 1. 存放新值的时候当前已有元素的个数必须大于等于阈值 2. ...

  5. 浅谈JAVA中HashMap、ArrayList、StringBuilder等的扩容机制

    JAVA中的部分需要扩容的内容总结如下:第一部分: HashMap<String, String> hmap=new HashMap<>(); HashSet<Strin ...

  6. 深入理解HashMap+ConcurrentHashMap的扩容策略

    前言 理解HashMap和ConcurrentHashMap的重点在于: (1)理解HashMap的数据结构的设计和实现思路 (2)在(1)的基础上,理解ConcurrentHashMap的并发安全的 ...

  7. 跟大佬一起读源码:CurrentHashMap的扩容机制

    并发编程——ConcurrentHashMap#transfer() 扩容逐行分析 前言 ConcurrentHashMap 是并发中的重中之重,也是最常用的数据结构,之前的文章中,我们介绍了 put ...

  8. HashMap的扩容机制以及默认大小为何是2次幂

    HashMap的Put方法 回顾HashMap的put(Key k, Value v)过程: (1)对 Key求Hash值,对n-1取模计算出Hash表数组下标 (2)如果没有碰撞,直接放入桶中,即H ...

  9. 【数组】- ArrayList自动扩容机制

    不同的JDK版本的扩容机制可能有差异 实验环境:JDK1.8 扩容机制: 当向ArrayList中添加元素的时候,ArrayList如果要满足新元素的存储超过ArrayList存储新元素前的存储能力, ...

随机推荐

  1. 前端之CSS样式

    一.CSS 1.什么是CSS 层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的 ...

  2. 基于SSM的单点登陆03

    TbUser.java和TbUserExample.java,TbUserMapper.java,TbUserMapper.xml由mybatis框架生成. generatorConfig.xml & ...

  3. Windows 端口和所提供的服务

    一 .端口大全 端口:0 服务:Reserved 说明:通常用于分析操作系统.这一方法能够工作是因为在一些系统中“0”是无效端口,当你试图使用通常的闭合端口连接它时将产生不同的结果.一种典型的扫描,使 ...

  4. [cqoi2012]交换棋子

      2668: [cqoi2012]交换棋子 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1334  Solved: 518[Submit][Stat ...

  5. Shell脚本报错--syntax error near unexpected token for((i=0;i<$length;i++))

    现象: shell脚本使用Nodepad++进行本地编辑,在编辑后上传到linux机器进行执行时提示“syntax error near unexpected token for((i=0;i< ...

  6. QT (QSS) 编程, QSS语法概述。。setstylesheet

    http://www.cnblogs.com/davesla/archive/2011/01/30/1947928.html 转载] QT皮肤(QSS)编程 借用css 的灵感, Qt也支持Qt自己的 ...

  7. 将本地jar包制作成maven依赖

    首先,需要配置maven环境,如果没配置,传送门:http://www.cnblogs.com/hyyq/p/6557683.html 然后,需要一个ben地的jar包,这里以阿里云支付宝sdk为例, ...

  8. Spring初学之annotation实现AOP前置通知、后置通知、返回通知、异常通知。

    实现两个整数的加减乘除.在执行每个方法之前打印日志. ArithmeticCalculator.java: package spring.aop.impl; public interface Arit ...

  9. Aix-Linux查看系统信息

    1.查看内存.磁盘等使用情况 命令:nmon 输入命令nmon之后,按M键memory,N键NetWork,J键查看目录使用情况. 2.查看防火墙 命令:smit 输入之后按照图形化操作 Commun ...

  10. JavaScript数组和函数的使用

    数组 数组:一个有顺序,有长度的数据集合 作用:存储大量数据 一.数组的定义 1.构造函数法:使用构造函数法的时候,都会使用new关键字 var arr=new Array(): 当长度为0 的时候, ...