1.7 ConcurrentHashMap 总结

底层结构

Segment 数组,每个 Segment 对象中包含一个 HashEntry 数组,数组元素可以是链表

原理

分段锁(Segment 继承自 ReetrantLock)

构造方法
// initialCapacity、concurrencyLevel 默认 16,loadFactor 默认 0.75
public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel)
  • 计算 Segment 数组的长度 ssize:大于等于 concurrencyLevel 的最小 2 的次幂;记录 segmentShift 和 segmentMask (用于插入元素时计算下标)
    在这里插入图片描述
  • 使用 initialCapacity 除以 ssize 计算每个Segment 对象中数组的长度 (最小是 2)在这里插入图片描述
  • 创建 Segment 数组,创建第 0 个位置的 Segment 对象
put 方法
  • key 和 value 不能为 null,否则抛出空指针异常
  • 计算 hash 值 (使用 key.hashCode() 和哈希种子计算)
  • 计算下标
    在这里插入图片描述
    即用 hash 值的高位和 length - 1 相与
  • 确保下标位置的 Segment 对象已经初始化 (使用了 CAS;以 segments[0] 为原型)
  • 调用 Segment 对象的 put 方法,put 的过程与 HashMap 类似 (Segment 算是一个小型的 HashMap),下标计算方式为:hash & length - 1,其中 hash 与计算大数组下标时的相同
    • 尝试获取锁,若获取失败则调用 scanAndLockForPut 方法,该方法会循环地调用 tryLock() 方法尝试获取锁,尝试一定次数后调用 lock() 方法阻塞地获取锁,确保最终一定获取到锁
    • 尝试获取锁的过程中会遍历链表 (若检测到头节点被其他线程修改则重新遍历并重置尝试次数),若不存在要插入的 key 则创建一个 HashEntry 对象
    • 获取到锁后遍历链表,若 key 存在则修改其 value,不存在则先判断是否需要扩容 (扩容条件为 插入后当前 Segment 中元素数量 > 容量 * 加载因子),然后插入新节点,若 scanAndLockForPut 方法返回的节点不为 null,则直接使用,否则 new 一个 HashEntry 对象插入 (头插法)
    • put 完成之后释放锁
扩容
  • 创建新数组(长度为原来的两倍)
  • 迁移数据:链表最后一段新下标相同的元素一次性移过去 (该段元素顺序不变),其他元素一个一个移 (头插法,顺序颠倒)
remove 方法
  • 找到所在的 Segment (与 put 方法相同)
  • 调用 Segment 对象的 remove 方法 (与 put 方法类似)
    • 尝试获取锁,若获取失败则调用 scanAndLock 方法,该方法会循环地调用 tryLock() 方法尝试获取锁,尝试一定次数后调用 lock() 方法阻塞地获取锁,确保最终一定获取到锁
    • 尝试获取锁的过程中会遍历链表 (若检测到头节点被其他线程修改则重新遍历并重置尝试次数),寻找要 remove 的 key
    • 获取到锁后遍历链表,删除对应节点 (若指定了 value 则必须 key 和 value 都相同才删除)
      在这里插入图片描述
    • remove 完成之后释放锁
size() 方法

循环遍历 segments,统计修改次数(modCount) 和元素个数(count):

  • 第一次遍历:记录修改次数初始值
  • 第二次遍历:若修改次数没变,则返回统计到的元素个数;若修改次数变了,则记录最新的修改次数,并继续循环,若第三次遍历后修改次数依然变了,则对所有的 Segment 对象加锁并继续循环遍历 (加锁后修改次数就不会变了)
  • 若某个 Segment 的 count 溢出了则返回 Integer.MAX_VALUE
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值