集合之ConcurrentHashMap

1小时让你了解ConcurrentHashMap

ConcurrentHashMap 可以做到既是线程安全的,同时也可以有很高的效率,得益于使用了分段锁。

实现原理

JDK 1.7:

ConcurrentHashMap 是通过数组 + 链表实现,由 Segment 数组和 Segment 元素里对应多个 HashEntry 组成

value 和链表都是 volatile 修饰,保证可见性

ConcurrentHashMap 采用了分段锁技术,分段指的就是 Segment 数组,其中 Segment 继承于 ReentrantLock

理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量)的线程并发,每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment

put 方法的逻辑较复杂:

尝试加锁,加锁失败 scanAndLockForPut 方法自旋,超过 MAX_SCAN_RETRIES 次数,改为阻塞锁获取

将当前 Segment 中的 table 通过 key 的 hashcode 定位到 HashEntry

遍历该 HashEntry,如果不为空则判断传入的 key 和当前遍历的 key 是否相等,相等则覆盖旧的 value

不为空则需要新建一个 HashEntry 并加入到 Segment 中,同时会先判断是否需要扩容

最后释放所获取当前 Segment 的锁

get 方法较简单:

将 key 通过 hash 之后定位到具体的 Segment,再通过一次 hash 定位到具体的元素上

由于 HashEntry 中的 value 属性是用 volatile 关键词修饰的,保证了其内存可见性

JDK 1.8:

抛弃了原有的 Segment 分段锁,采用了 CAS + synchronized 来保证并发安全性

HashEntry 改为 Node,作用相同

val next 都用了 volatile 修饰

put 方法逻辑:

根据 key 计算出 hash 值

判断是否需要进行初始化

根据 key 定位出的 Node,如果为空表示当前位置可以写入数据,利用 CAS 尝试写入,失败则自旋

如果当前位置的 hashcode == MOVED == -1,则需要扩容

如果都不满足,则利用 synchronized 锁写入数据

如果数量大于 TREEIFY_THRESHOLD 则转换为红黑树

get 方法逻辑:

根据计算出来的 hash 值寻址,如果在桶上直接返回值

如果是红黑树,按照树的方式获取值

如果是链表,按链表的方式遍历获取值

JDK 1.7 到 JDK 1.8 中的 ConcurrentHashMap 最大的改动:

链表上的 Node 超过 8 个改为红黑树,查询复杂度 O(logn)

ReentrantLock 显示锁改为 synchronized,说明 JDK 1.8 中 synchronized 锁性能赶上或超过 ReentrantLock

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值