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. CMD 删除脚本

    CMD 删除脚本 forfiles /p D:\BACKUP\WindowsImageBackup /s /m *.* /d -14 /c "cmd /c del @file"; ...

  2. 20145230 《Java程序设计》第8周学习总结

    20145230 <Java程序设计>第8周学习总结 教材学习内容 NIO与NIO2 NIO使用频道(Channel)来衔接数据节点,在处理数据时,NIO可以设定缓冲区(Buffer)容量 ...

  3. java转义符的一些用法

    那么这里在列上一些转义字符 \\ 反斜杠 \t 间隔 ('\u0009')\n 换行 ('\u000A')\r 回车 ('\u000D')\d 数字 等价于 [0-9]\D 非数字 等价于 [^0-9 ...

  4. 发现程序bug思路

    大家有没有遇到过项目,程序出现个bug,但花了好久(真的是a long long time啊)才发现引发这个问题的原因,心想原来就这个原因导致的啊,要是早想到就好了! 其实我们确实的是方法,希望我的抛 ...

  5. Tomcat学习之Wrapper

    Tomcat学习之Wrapper 分类: WEB服务器2012-08-30 22:16 1547人阅读 评论(0) 收藏 举报 tomcatservletwrapperservletslistexce ...

  6. 关于nginx性能优化及基本概念

    参考文章: Nginx面试中最常见的18道题:http://blog.csdn.net/liyanlei5858/article/details/77924420 Nginx性能优化指南:http:/ ...

  7. uvalive 6932

    三个串必须要一起dp 之前刚学了dfs的记忆化搜索的dp方式 觉得很舒服 现学现卖然后两个小时都没有做出来 优化1:之前在dfs中 对每一个pos都会枚举所有可能的组合 结合当前状态来产生新的状态 来 ...

  8. vc 自删除

    // delself.cpp : 定义控制台应用程序的入口点.// #include "stdafx.h"#include <windows.h>#include &l ...

  9. Spring初学之通过工厂方法配置Bean

    工厂方法配置bean分为两种,一种是实例工厂方法,另一种是静态工厂方法. 先来看看实体bean: Car.java: package spring.beans.factory; public clas ...

  10. Memcached之缓存雪崩,缓存穿透,缓存预热,缓存算法

    缓存雪崩 缓存雪崩可能是因为数据未加载到缓存中,或者缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机. 解决思路: 1,采用加锁计数,或者使用合理的队列 ...