ConcurrentHashMap 源码阅读小结
前言
每一次总结都意味着重新开始,同时也是为了更好的开始。ConcurrentHashMap 一直是我心中的痛。虽然不敢说完全读懂了,但也看了几个重要的方法,有不少我觉得比较重要的知识点。
然后呢,放一些楼主写的关于 ConcurrentHashMap 相关源码分析的文章链接:
- ConcurrentHashMap 扩容分析拾遗
- 并发编程——ConcurrentHashMap#addCount() 分析
- 并发编程——ConcurrentHashMap#transfer() 扩容逐行分析
- 并发编程——ConcurrentHashMap#helpTransfer() 分析
- 并发编程 —— ConcurrentHashMap size 方法原理分析
- 并发编程之 ConcurrentHashMap(JDK 1.8) putVal 源码分析
- 深入理解 HashMap put 方法(JDK 8逐行剖析)
- 深入理解 hashcode 和 hash 算法
putVal 方法总结
说起 ConcurrentHashMap ,当然从入口开始说。该方法要点如下:
- 不允许有 null key 和 null value。
- 只有在第一次 put 的时候才初始化 table。初始化有并发控制。通过 sizeCtl 变量判断(小于 0)。
- 当 hash 对应的下标是 null 时,使用 CAS 插入元素。
- 当 hash 对应的下标值是 forward 时,帮助扩容,但有可能帮不了,因为每个线程默认 16 个桶,如果只有 16个桶,第二个线程是无法帮助扩容的。
- 如果 hash 冲突了,同步头节点,进行链表操作,如果链表长度达到 8 ,分成红黑树。
- 调用 addCount 方法,对 size 加一,并判断是否需要扩容(如果是覆盖,就不调用该方法)。
- Cmap 的并发性能是 hashTable 的 table.length 倍。只有出现链表才会同步,否则使用 CAS 插入。性能极高。
size 方法总结
- size 方法不准确,原因是由于并发插入,baseCount 难以及时更新。计数盒子也难以及时更新。
- 内部通过两个变量,一个是 baseCount,一个是 counterCells,counterCells 是并发修改 baseCount 后的备用方案。
- 具体更新 baseCount 和 counterCells 是在 addCount 方法中。备用方法 fullAddCount 则会死循环插入。
- CounterCell 是一个用于分配计数的填充单元,改编自 LongAdder和Striped64。内部只有一个 volatile 的 value 变量,同时这个类标记了
@sun.misc.Contended,这是一个避免伪共享的注解,用于替代之前的缓存行填充。多线程情况下,注解让性能提升 5 倍。
helpTransfer 方法总结
- 当 Cmap 尝试插入的时候,发现该节点是 forward 类型,则会帮助其扩容。
- 每次加入一个线程都会将 sizeCtl 的低 16 位加一。同时会校验高 16 位的标示符。
- 扩容最大的帮助线程是 65535,这是低 16 位的最大值限制的。
- 每个线程默认分配 16 个桶,如果桶的数量是 16,那么第二个线程无法帮助其扩容。
transfer 方法总结
- 该方法会根据 CPU 核心数平均分配给每个 CPU 相同数量的桶。但如果不够 16 个,默认就是 16 个。
- 扩容是按照 2 倍进行扩容。
- 每个线程在处理完自己领取的区间后,还可以继续领取,如果有的话。这个是 transferIndex 变量递减 16 实现的。
- 每次处理空桶的时候,会插入一个 forward 节点,告诉 putVal 的线程:“我正在扩容,快来帮忙”。但如果只有 16 个桶,只能有一个线程扩容。
- 如果有了占位符,那就不处理,跳过这个桶。
- 如果有真正的实际值,那就同步头节点,防止 putVal 那里并发。
- 同步块里会将链表拆成两份,根据 hash & length 得到是否是 0,如果是0,放在低位,反之,反之放在 length + i 的高位。这里的设计是为了防止下次取值的时候,hash 不到正确的位置。
- 如果该桶的类型是红黑树,也会拆成 2 个,这是必须的。然后判断拆分过的桶的大小是否小于等于 6,如果是,改成链表。
- 线程处理完之后,如果没有可选区间,且任务没有完成,就会将整个表检查一遍,防止遗漏。
addCount 方法总结
- 当插入结束的时候,会对 size 进行加一。也会进行是否需要扩容的判断。
- 优先使用计数盒子(如果不是空,说明并发了),如果计数盒子是空,使用 baseCount 变量。对其加 X。
- 如果修改 baseCount 失败,使用计数盒子。如果此次修改失败,在另一个方法死循环插入。
- 检查是否需要扩容。
- 如果 size 大于等于 sizeCtl 阈值,且长度小于 1 << 30,可以扩容成 1 << 30,但不能扩容成 1 << 31。
- 如果已经在扩容,帮助其扩容,和 helpTransfer 逻辑一样。
- 如果没有在扩容,自行开启扩容,更新 sizeCtl 变量为负数,赋值为标识符高 16 位 + 2。
小结
ConcurrentHashMap 满是财富,都是精华代码,我们这次阅读只是管中窥豹,要知道其中包含 53 个类,6300 行代码,但这次确实收获很多。有时间一定再次阅读!!
能力不高,水平有限,有些地方确实理解不了 Doug Lea 大师的设计,如果有什么错误,还请大家指出。不胜感激。
ConcurrentHashMap 源码阅读小结的更多相关文章
- JDK12 concurrenthashmap源码阅读
本文部分照片和代码分析来自文末参考资料 java8中的concurrenthashmap的方法逻辑和注解有些问题,建议看最新的JDK版本 建议阅读 concu ...
- ConcurrentHashMap源码阅读
1. 前言 HashMap是非线程安全的,在多线程访问时没有同步机制,并发场景下put操作可能导致同一数组下的链表形成闭环,get时候出现死循环,导致CPU利用率接近100%. HashTable是线 ...
- JDK1.8 ConcurrentHashMap源码阅读
1. 带着问题去阅读 为什么说ConcurrentHashMap是线程安全的?或者说 ConcurrentHashMap是如何防止并发的? 2. 字段和常量 首先,来看一下ConcurrentHa ...
- [PHP源码阅读]explode和implode函数
explode和implode函数主要用作字符串和数组间转换的操作,比如获取一段参数后根据某个字符分割字符串,或者将一个数组的结果使用一个字符合并成一个字符串输出.在PHP中经常会用到这两个函数,因此 ...
- 【原】SDWebImage源码阅读(五)
[原]SDWebImage源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 前面的代码并没有特意去讲SDWebImage的缓存机制,主要是想单独开一章节专门讲 ...
- EventBus源码解析 源码阅读记录
EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...
- 【跟着子迟品 underscore】Object Functions 相关源码拾遗 & 小结
Why underscore 最近开始看 underscore.js 源码,并将 underscore.js 源码解读 放在了我的 2016 计划中. 阅读一些著名框架类库的源码,就好像和一个个大师对 ...
- SparkConf加载与SparkContext创建(源码阅读一)
即日起开始spark源码阅读之旅,这个过程是相当痛苦的,也许有大量的看不懂,但是每天一个方法,一点点看,相信总归会有极大地提高的.那么下面开始: 创建sparkConf对象,那么究竟它干了什么了类,从 ...
- 初始化IoC容器(Spring源码阅读)
初始化IoC容器(Spring源码阅读) 我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? ...
随机推荐
- HDU 4920 矩阵乘积 优化
Matrix multiplication Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/ ...
- EBS-如何查看非自己提交的请求的结果
http://www.cnblogs.com/quanweiru/p/4692071.html 如何查看非自己提交的请求的结果定位要找的请求SQL举例:SELECT req.request_id, ...
- 两台linux之间建立信任关系,实现免密码ssh远程登录或scp数据上传
两台linux之间建立信任关系,实现免密码远程登录或数据上传 1.执行ssh-keygen命令,生成建立安全信任关系的证书: linux1上:执行命令 ssh-keygen -t rsa 在程序提 ...
- 关于GROUP BY和聚合函数
可以这样去理解group by和聚合函数 转自 http://www.cnblogs.com/wiseblog/articles/4475936.html 写在前面的话:用了好久group by,今天 ...
- Git - 信息查看
git help git version # Display the version of git. git help # Prints the synopsis and a list of the ...
- iOS,Android,WP, .NET通用AES加密算法
这两天为移动App开发API,结果实现加密验证时碰到一大坑.这里不得不吐槽下又臭又硬的iOS,Windows Server无法解密出正确的结果,Android则可以,后来使用了通用的AES256加密算 ...
- Django两天搭建个人博客
传送门:https://github.com/1417766861/django-blog(可直接运行,上面有步骤) 效果: 首页: 侧栏: 详情页面: 修改头像,资料,文章发布: 支持添加标签拖拽 ...
- C#递归方法遍历目录及子目录
众所周知,获得某一目录下第一级的所有文件和文件夹列表,很容易办到:DirectoryInfo di=new DirectoryInfo(strBaseDir);//strBaseDir是起始目录,绝对 ...
- Django模版语言自定义标签-实现前端 关联组合过滤查询
前端关联 组合过滤查询 实现效果如图: models.py 创建表代码 from django.db import models # Create your models here. class Le ...
- Python中的 // 与 / 的区别
" / " 表示浮点数除法,返回浮点结果;" // " 表示整数除法,返回不大于结果的一个最大的整数 [code] print("6 // 4 = & ...