hashmap是线程安全的吗?怎么解决?_你真的了解线程安全的ConcurrentHashMap的实现吗?...

可能这样的面试场景比较常见:

面试官:ConcurrentHashMap是怎么实现的呢?

程序猿小僧:ConcurrentHashMap是线程安全的HashMap,基于分段锁实现,

面试官:分段锁又是怎么实现的呢?

程序员小僧:这个。。。

进入正题,要说ConcurrentHashMap的实现,那得从两个JDK版本说起!

JDK1.7的实现

ConcurrentHashMap的数据结构是由一个HashEntry数组和多个Segment组成,主要实现原理是通过锁分离的思路解决了多线程的安全问题,如下图所示:

2a505639f7e1b1c2e0fc75629d7e1fef.png

ConcurrentHashMap的数据结构

这里简单总结下:

1、通过两次hash算法将Entry数组最终存储在段(Segment)中的数组活链表里,Segment的Size永远是2的幂,因为在计算Segment的Size时使用了左移运算符<<

2、由于Segment继承ReentrantLock,所以在put时通过ReentrantLock的tryLock()方法尝试去获取锁,如果获取成功就直接插入相应的位置,如果已经有线程获取该Segment的锁,那当前线程会以自旋的方式(自旋就是一个循环获取锁的过程)继续调用tryLock()方法去获取锁,超过指定次数就挂起,等待唤醒.

3、get操作跟HashMap类似,只是ConcurrentHashMap第一次需要经过一次hash定位到Segment的位置,然后再hash定位到指定的HashEntry,遍历该HashEntry下的链表进行对比,成功就返回,不成功就返回null

JDK1.8的实现

没有继续使用Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,虽然在JDK1.8中还能看到Segment,它只是为了兼容旧版本而已.

1、锁的粒度降低为一个Entry,而不是1.7中的多个Entry组成的Segment,

2、当链表长度大于8时,使用红黑树来优化链表,以达到O(logn)的遍历复杂度

3、由于Synchronized在1.6被优化成了一个锁粒度的升级(自旋锁->偏向锁->轻量级锁->重量级锁),而非重量级锁,从而提高并发的效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值