03、ConcurrentHashMap怎么保证HashMap的线程安全呢?

目录

【jdk 1.7】

【jdk 1.8】

【CAS】


【jdk 1.7】

 

jdk 1.7中 ConcurrentHashMap的数据结构是:ReentrantLock+Segment+HashEntry

Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,保证了数据安全,而每一个Segment元素存储的是 数组+链表,这个和HashMap的数据存储结构一样;

当我们去put数据的时候,经过两次hashcode操作,去定位存储的位置;
1.第一次根据key计算hashcode值,锁定我要存储的Segment;
2.第二次根据key计算hashcode值,锁定我要存储的hashMap的数组位置
Segment实现了ReentrantLock(乐观锁),也就带有锁的功能,保证了数据安全;

当我们去get数据的时候,也是经过两次hashcode操作,
1.第一次根据key计算hashcode值,锁定我要取值的Segment
2.第二次根据key计算hashcode值,锁定我要取值的hashMap的数组位置

然后,定位查找的位置,遍历HashEntry中链表进行equals对比查询;

 

这样操作保证了数据安全,但是也存在一些弊端:

当某个Segment下面的数据量【桶】越来越大时,对应的查询效率和更新效率会变低;
当某个Segment进行加锁时,修改某个桶中的数据,这个Segment下面其他桶的数据就都被加锁。
就会导致更新效率变低,最终导致我们并发量变低。

【jdk 1.8】

 

jdk 1.8 中,ConcurrentHashMap的数据结构是:

Node数组 + 链表 + 红黑树 + synchronized + CAS;

取消了segment 分段锁,也就意味着不需要进行二次hash,就可以找到对应的Node了;

而Node 是 ConcurrentHashMap存储结构的基本单元,继承了HashMap中的Entry,用于存储数据;

put操作
根据key进行hashCode值计算,找到对应的Node,如果为空表示当前位置可以写入数据,利用CAS尝试写入,失败则自旋保证成功,如果非空,则利用synchronized锁写入数据

这样操作的好处是:
优化了底层数据结构,降低了锁的粒度,仅仅使用一次hash就能获取到桶的位置,提高了执行和并行效率,提高吞吐量;

【CAS】

【自旋锁】
CAS 比较和替换 compare and swap

是乐观锁思想的一种实现方式,JUC中很多工具类都是基于CAS的
简单描述:
当你去修改数据时,我认为数据是没有被修改过的,我直接尝试执行修改操作,
当真正修改数据时,才去比较我们的数据有没有发生变化,
如果发生变化,修改失败,如果没有发生变化,直接修改成功
【这样的好处就是:无锁操作,没有加锁的逻辑,执行效率更高,消耗的内存更少】

为什么会有不相等的情况?

这是因为在修改之前,别人线程进行修改了
(单线程不会出现这种问题)

【预期值】:从内存中拿到的值
【内存值】:内存中存储的值【可能会被其他线程修改】
步骤:
1--先从内存中获取数据,拿到预期值
2--我直接尝试修改
3--修改的时候比较内存值和预期值
4--如果相等,直接进行修改;
不相等,自选获取最新的内存值,作为预期值,再次尝试修改,直到修改成功。

并非这里真的有一把锁,而是用了一些逻辑,实现了一种类似于锁的功能

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值