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) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? ...
随机推荐
- ASP.NET Web API 异常处理 HttpResponseException 以及Angularjs获取异常信息并提示
一.HttpResponseException 如果一个Web API控制器抛出一个未捕捉异常,默认地,大多数异常都会被转化成一个带有状态码“500 – 内部服务器错误”的HTTP响应.HttpRes ...
- ASP.NET Web API 框架研究 ASP.NET 路由
ASP.NET Web API 如果采用Web Host方式来寄宿,在请求进入Web API 消息处理管道之前,就会用ASP.NET 自身的路由系统根据注册的路由表,解析出当前请求的HttpContr ...
- Android-AndroidStudio加载工程方式-gradle文件夹
例如:在其他地方,其他工作人员哪里的OpenGateDemo工程是OK的, 然后Copy到李四的电脑上运行是报错,其实所有的错误都和gradle有关: 第一步,李四电脑运行OpenGateDemo工程 ...
- Android-Java-面向对象与面向过程的简单理解
支持面向过程的语言有:C Basic 等语言: 支持面向对象的语言有:C++ Java C# 等语言: 面向过程:操作的是行为/功能: 面向对象:操作的是对象,而对象里面有功能行为,所以可以指定 ...
- 关于能量场和力场弯曲空间的实验证明 EXPERIMENTAL PROOF ON THE BENDING SPACE OF ENERGY FIELD AND FORCE FIELD
前文提到,F = ma, E = mc^2,并且等效是传递的,等效概念具有同属性.所以不止能量,力场也可以弯曲空间. 实验:某人被头顶上方的电站10几万伏的设备吸收,烧毁双臂. (虽然这个实验不应具有 ...
- 中科大debian 9 + docker源设置
wget https://mirrors.ustc.edu.cn/repogen/conf/debian-http-4-stretch -O sources.list sudo apt-get ins ...
- JQuery - 阻止回车键
JQuery 和 js 禁止enter回车事件方法 jQuery版 $(window).keydown( function(e) { var key = window.event?e.keyCode: ...
- 下拉框select中option居中样式
下拉框select中option居中样式 text-align:center;text-align-last:center;
- Code Chef MINPOLY(计算几何+dp)
题面 传送门 题解 我们枚举这个凸多边形\(y\)坐标最小的点\(p_i\),然后对于所有\(y\)坐标大于等于它的点极角排序 我们预处理出\(s_{j,k}\)表示三角形\(p_i,p_j,p_k\ ...
- [学习笔记]K-D Tree
以前其实学过的但是不会拍扁重构--所以这几天学了一下 \(K-D\ Tree\) 的正确打开姿势. \(K\) 维 \(K-D\ Tree\) 的单次操作最坏时间复杂度为 \(O(k\times n^ ...