19 ConcurrentHashMap1.7
Posted cathy_mu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了19 ConcurrentHashMap1.7相关的知识,希望对你有一定的参考价值。
一个concurrentHashMap是一个segment数组
一个segment里是个hashEntry数组
对不同的segment进行操作无需考虑锁竞争
segment也有负载因子、阈值,segment的数量是2的幂,容量也是2的幂。
concurrentHashmap的put:
1.定位到segment
2.如果segment为空,初始化segment
3.对segment调用put
初始化segment
- 检查计算得到的位置的 Segment 是否为null.
- 为 null 继续初始化,使用 Segment[0] 的容量和负载因子创建一个 HashEntry 数组。
- 再次检查计算得到的指定位置的 Segment 是否为null.
- 使用创建的 HashEntry 数组初始化这个 Segment.
- 自旋判断计算得到的指定位置的 Segment 是否为null,使用 CAS 在这个位置赋值为 Segment.
put
- tryLock() 获取锁,获取不到使用 scanAndLockForPut 方法继续获取。
- 计算 put 的数据要放入的 index 位置,然后获取这个位置上的 HashEntry 。
遍历 put 新元素,为什么要遍历?因为这里获取的 HashEntry 可能是一个空元素,也可能是链表已存在,所以要区别对待。
如果这个位置上的 HashEntry 不存在:
- 如果当前容量大于扩容阀值,小于最大容量,进行扩容。
- 直接头插法插入。
如果这个位置上的 HashEntry 存在:
- 判断链表当前元素 Key 和 hash 值是否和要 put 的 key 和 hash 值一致。一致则替换值
不一致,获取链表下一个节点,直到发现相同进行值替换,或者链表表里完毕没有相同的。
- 如果当前容量大于扩容阀值,小于最大容量,进行扩容。
- 直接链表头插法插入。
- 如果要插入的位置之前已经存在,替换后返回旧值,否则返回 null.
get方法无需加锁,由于其中涉及到的共享变量都使用volatile修饰,volatile可以保证内存可见性,所以不会读取到过期数据。
假设有2^N个segment,那根据元素的高N位就可以判断元素在哪个segment中。
对volatile字段的写入操作happens-before于每一个后续的同一个字段的读操作。
每个HashEntry中的next也是final的,没法对链表最后一个元素增加一个后续entry所以新增一个entry的实现方式只能通过头结点来插入了。
我们要删除 e3这个entry,因为HashEntry中next的不可变,所以我们无法直接把e2的next指向e4,而是将要删除的节点之前的节点复制一份,形成新的链表。
get分两步:1.看看segment中entry的个数,2.找到entry时判断下value是否为空。这种机制如何保证另一个线程增加、删除、修改entry时,本线程不受影响?
1.增加:如果查找时对方对象还没有创建好,value会为空。
2.修改:volitale保证了读到的会是对方写好的
3.删除:看到了对方还没来得及删的问题也不大
以上是关于19 ConcurrentHashMap1.7的主要内容,如果未能解决你的问题,请参考以下文章
19.7 主动模式和被动模式 19.8 添加监控主机 19.9 添加自定义模板 19.10 处理图形
19.7 主动模式和被动模式 19.8 添加监控主机 19.9 添加自定义模板 19.10 处理图形
19.1 Linux监控平台介绍 19.2 zabbix监控介绍 19.3/19.4/19.6 安装
19.1 Linux监控平台介绍 19.2 zabbix监控介绍 19.3/19.4/19.6 安装zabbix 19.5 忘记Admin密码如何做