ConcurrentHashMap在jdk1.7和jdk1.8中的部分差异


前言

大家好, 这里是Yve菌, 今天给大家总结一些ConcurrentHashMap在jdk1.7和jdk1.8中的区别


ConcurrentHashMap在jdk1.7和1.8中的区别

1. 结构不同

ConcurrentHashMap在jdk1.7中使用的是Segment+HashEntry的结构, segment继承了ReentrantLock在结构中充当锁的角色, 一个segment中根据情况会存在多个HashEntry, 元素会以链表的方式存放在HashEntry中
在这里插入图片描述
在1.8中, ConcurrentHashMap放弃了Segment+HashEntry臃肿的设计, 改为使用Node+Synchronized+CAS的结构. Node的结构相当于1.7的HashEntry, 但HashEntry是内部类.使用synchronized+CAS代替segment这样锁的粒度更小了,并且不是每次都需要加锁, 只有CAS失败再加锁
在这里插入图片描述

2. put()方法

在1.7中因为存在segment, 所以在put时需要先通过key的rehash的高位segment长度-1进行与运算得到对应segment下标; 再通过key的rehash值table数组长度-1进行与运算获得对应table的下标. 当有线程在进行操作时会将segment锁住, 其他线程在要put发现segment被锁住时可以做一些put前的准备工作.

而在1.8中, 会根据key的rehash值和数组长度进行与运算得到对应下标后的first节点, 之后:

  • 如果为null则尝试通过cas插入
  • 如果不为null并且first.hash == -1, 说明其他线程正在扩容, 参与协助扩容
  • 如果不为null并且first.hash != -1, synchronized锁住first节点, 判断为链表还是红黑树, 之后遍历插入

3. resize()方法

在1.7中每次扩容只会扩容segment内部的HashEntry, 而segment本身并不会扩容, 在需要扩容获得锁之后, 在单线程中去做扩容

而在1.8中扩容支持并发节点转移, 在线程put时发现有其他线程进行扩容时会协助节点转移, 从数组尾部开始, 如果该位置已经被处理过了就会创建一个ForwardingNode放到首节点, hash值设为-1, 这样其他线程就知道该位置被处理过了

4. 计算size

在1.7中会计算两次size, 如果两次结果一样说明是正确的, 直接返回. 如果两次结果不一样则会把所有的segment锁住之后重新计算所有segment的count总和.

在1.8中由于没有segment的概念, 所以存在一个baseCount来记录节点个数, 如果出现多线程竞争的情况开启一个countercells数组, 其他线程记录变化的值会被加载到对应的coutercell位置中, 最后统计baseCount和countercells的总和.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值