jdk8中的ConcurrentHashMap究竟为什么高效?

 我来答
华源网络
2022-06-22 · TA获得超过5588个赞
知道小有建树答主
回答量:2486
采纳率:100%
帮助的人:146万
展开全部
从源码来窥其一斑!

我们都知道hashMap不是线程安全的,因为在扩容方法中很容易出现死循环,hashTable使用锁的方式比较简单暴力,几乎在所有操作方法上都加了synchronized锁,导致总体性能很差,concurrentHashmap凭借线程安全且性能优异一直都是高并发中的首选key-value型数据结构;

concurrentHashmap的高性能有以下原因:

一,分段锁:jdk8中对concurrentHashmap进行了改进,抛弃了jdk7中新建segment作为分段锁的过程,jdk8中虽沿用了这种分段锁的思想,却直接使用数组中的数据作为 分段锁保证concurrentHashmap在上锁的时候只针对数组下标下的数据进行上锁 (比如如果数组长度为256,那么每次put平均只有1/256的数据被锁),而大多数其他的数据还是能进行正常的增删改操作,无需阻塞等待,这无疑极大的 降低了锁的粒度,提升了性能。

二,红黑树 :jdk8中引入了红黑树结构,在单个数组下标内的数据达到8以后,会自动转换为红黑树进行存储, 使用大O表示法表示效率的话,红黑树的查找效率为O(log(n)),而链表的效率为O(n) ,当数据量越来越大的时候,红黑树的效率明显好于链表,所以concurrentHashmap性能得到很大提升;

现在我们主要从put方法中的主要方法来分析性能的提升:

spread(key.hashCode());//作用是再次哈希,减少冲突 ,源码如下

其中涉及到的位运算有

>>> 16:无符号右移16位,空位以0补齐 。

^:异或运算符-->相同为0,不同为1; &:与运算符-->全1得1,否则0;

(h ^ (h >>> 16)) & HASH_BITS; 所以这句代码的意思就是不仅消除高16位的影响,同时获得正整数的hash值

再来看后面的方法, 如上图:

1,就是判断当这个hash表还是空的时候,调用initTable进行初始化; 2,使用(n - 1) & hash)计算数组下标,如果数据指定下标处为null,则直接插入,注: cas是java8中的concurrentHashmap引入的线程安全判断,CAS算法做为乐观锁 ;

3,(fh = f.hash) == MOVED,走到此处说明下标内有node,且该node的值为-1(MODED=-1),搜索全类发现MODED是在调用有参构造器ForwardingNode中默认写入的,而这个调用处刚好在transfer方法中,所以我们推断,扩容的时候先将数组下标内的node.hash置为-1! 同时在3这一步中调用helpTransfer(tab, f)参与扩容,并把数据写入;

4,走到这说明node不是空的,也没在扩容,那么锁住该下标下的node,并把新value插入链表中; 5,如果锁住的这个node能实例化为TreeBin,则代表已经转化为红黑树进行存储,将数据插入红黑树中; 6,判断在4,5中计算得到的数组下标内所有节点总数, 如果满足转化为红黑树的条件(节点数大于8),则自动转化为红黑树进行存储!

总的来说,concurrentHashmap之所以性能高就是因为使用了分段锁和红黑树!

至于conrrentHashmap其他的方法的源码分析,后期会补上的,更多的技术分享,敬请关注!
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式