ConcurrentPrograming——ConcurrentHashMap



一、JDK1.7ConcurrentHashMap

1、原理

它维护了一个segment数组,每个segment对应一把锁
加锁用ReentraLock加在segment对象上
优点:多个线程访问不同的segment,没有冲突
缺点:Segment数组默认大小文16,这个容量初始化指定后就不能改变了,而且不是懒惰初始化。
在这里插入图片描述
构造完成,会形成下列的Segments数组
在这里插入图片描述
ConcurrentHashMap没有实现懒惰初始化,占用空间不友好。
其中 this.segmentShift 和 this.segmentMask 的作用是决定将 key 的 hash 结果匹配到哪个 segment
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、JDK1.7下的并发死链:

假设有两个线程,线程1和线程2,两个线程进行hashMap的put操作,触发了扩容。
在这里插入图片描述
线程1和线程2 都进入if,然后线程1没有拿到cpu的资源在上面代码注释的地方停下了。此时的变量指针如下图所示:

在这里插入图片描述
记住 线程1中 E变量指向a结点,next变量指向b结点。
下面是线程2 拿到cpu的资源,执行结点转移
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
线程2停下,轮到线程1
因为之前线程1中E变量指向的是a结点,next变量指向的是b结点,所以如下图所示:
在这里插入图片描述

再来看看 刚才线程是在e.next = newTable[i] 这句代码还没执行的时候停下的,那么现在就要执行这一句代码
在这里插入图片描述
时线程1 执行代码之后,就造成了链表的死循环,结果如下:
在这里插入图片描述
原因就是线程一暂停了,线程二又跑完了流程,把原来桶下标的引用发生了变化,扩容时原来位于头节点的元素,先被遍历,所以扩容时,位于新表的尾结点后来的元素位于头结点,扩容时发生的反转,出现了原来的e(a)指向next(b),然后b在此循环时b是e,next指向a,使得自己指向自己形成了并发死链。

二、JDK1.8ConcurrentHashMap

JDK1.8出现的红黑树,可以防止dos(攻击者构造一大批hashcode一样的对象,往map、填充,造成性能直线下降)的攻击,map容量大于64,链表长度大于八的时候会转换为红黑树。用synoc加锁加在链表头,提升性能。随着哈希表扩容,链表个数会越来越多,并发度随着容量变大会更高。而且是懒惰初始化。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、总结

以上内容为个人总结以及网上学习视频总结出来的比较,目的是为了方便加深大家对ConcurrentHashMap的理解,图片若有侵权,必删。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值