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

  1. tryLock() 获取锁,获取不到使用 scanAndLockForPut 方法继续获取。
  2. 计算 put 的数据要放入的 index 位置,然后获取这个位置上的 HashEntry 。
  3. 遍历 put 新元素,为什么要遍历?因为这里获取的 HashEntry 可能是一个空元素,也可能是链表已存在,所以要区别对待。

    • 如果这个位置上的 HashEntry 不存在:

      1. 如果当前容量大于扩容阀值,小于最大容量,进行扩容。
      2. 直接头插法插入。
    • 如果这个位置上的 HashEntry 存在:

      1. 判断链表当前元素 Key 和 hash 值是否和要 put 的 key 和 hash 值一致。一致则替换值
      2. 不一致,获取链表下一个节点,直到发现相同进行值替换,或者链表表里完毕没有相同的。

        1. 如果当前容量大于扩容阀值,小于最大容量,进行扩容。
        2. 直接链表头插法插入。
  4. 如果要插入的位置之前已经存在,替换后返回旧值,否则返回 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章 E4X

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密码如何做

19.12 添加自定义监控项目 19.13/19.14 配置邮件告警 19.15 测试告警