JDK 源码复习 concurrent 包 concurrentHashMap 01

https://baijiahao.baidu.com/s?id=1617089947709260129&wfr=spider&for=pc

https://www.cnblogs.com/zerotomax/p/8687425.html

https://www.cnblogs.com/yangming1996/p/8031199.html

https://www.cnblogs.com/banjinbaijiu/p/9147434.html

详细分析如下url

https://blog.csdn.net/programmer_at/article/details/79715177#2-concurrenthashmap-%E5%9C%A817%E4%B8%8E18%E4%B8%AD%E7%9A%84%E4%B8%8D%E5%90%8C

1、ConcurrentHashMap跟HashMap,HashTable的对比

  我们都知道HashMap不是线程安全的,所以在处理并发的时候会出现问题。

  而HashTable虽然是线程安全的,但是是通过整个来加锁的方式,当一个线程在写操作的时候,另外的线程则不能进行读写。

  而ConcurrentHashMap则可以支持并发的读写。跟1.7版本相比,1.8版本又有了很大的变化,已经抛弃了Segment的概念,虽然源码里面还保留了,也只是为了兼容性的考虑。

2、ConcurrentHashMap原理概览

   在ConcurrentHashMap中通过一个Node<K,V>[]数组来保存添加到map中的键值对,而在同一个数组位置是通过链表和红黑树的形式来保存的。但是这个数组只有在第一次添加元素的时候才会初始化,否则只是初始化一个ConcurrentHashMap对象的话,只是设定了一个sizeCtl变量,这个变量用来判断对象的一些状态和是否需要扩容,后面会详细解释。

  第一次添加元素的时候,默认初期长度为16,当往map中继续添加元素的时候,通过hash值跟数组长度取与来决定放在数组的哪个位置,如果出现放在同一个位置的时候,优先以链表的形式存放,在同一个位置的个数又达到了8个以上,如果数组的长度还小于64的时候,则会扩容数组。如果数组的长度大于等于64了的话,在会将该节点的链表转换成树

   同一位置,个数大于8个,并且数组长度大于等于64,才把节点的链表转换成为红黑树

  通过扩容数组的方式来把这些节点给分散开。然后将这些元素复制到扩容后的新的数组中,同一个链表中的元素通过hash值的数组长度位来区分,是还是放在原来的位置还是放到扩容的长度的相同位置去 。在扩容完成之后,如果某个节点的是树,同时现在该节点的个数又小于等于6个了,则会将该树转为链表。

如果扩容后,某个节点的个数小于等于6个,会从红黑树退化成链表

  取元素的时候,相对来说比较简单,通过计算hash来确定该元素在数组的哪个位置,然后在通过遍历链表或树来判断key和key的hash,取出value值。

  往ConcurrentHashMap中添加元素的时候,里面的数据以数组的形式存放的样子大概是这样的:

 

几个重要的类

Node<K,V>,这是构成每个元素的基本类。

5、ConcurrentHashMap的初始化

   首先我们看看构造方法

可以看到,在任何一个构造方法中,都没有对存储Map元素Node的table变量进行初始化。而是在第一次put操作的时候在进行初始化。下面来看看数组的初始化方法initTable

6、ConcurrentHashMap的put操作详解

  下面看看put方法的源码

 7、ConcurrentHashMap的扩容详解

   在put方法的详解中,我们可以看到,在同一个节点的个数超过8个的时候,会调用treeifyBin方法来看看是扩容还是转化为一棵树

  同时在每次添加完元素的addCount方法中,也会判断当前数组中的元素是否达到了sizeCtl的量,如果达到了的话,则会进入transfer方法去扩容

在tryPresize方法中,并没有加锁,允许多个线程进入,如果数组正在扩张,则当前线程也去帮助扩容。

数组扩容的主要方法就是transfer方法

 

到这里,ConcurrentHashMap的put操作和扩容都介绍的差不多了,

  下面的两点一定要注意:

    ·复制之后的新链表不是旧链表的绝对倒序。

    ·在扩容的时候每个线程都有处理的步长,最少为16,在这个步长范围内的数组节点只有自己一个线程来处理

8、ConcurrentHashMap的get操作详解

  相比put操作,get操作就显得很简单了。废话少说,直接上源码分析。

 那么,多个线程又是如何同步处理的呢?

  在ConcurrentHashMap中,同步处理主要是通过Synchronized和unsafe两种方式来完成的。

  ·在取得sizeCtl、某个位置的Node的时候,使用的都是unsafe的方法,来达到并发安全的目的

  ·当需要在某个位置设置节点的时候,则会通过Synchronized的同步机制来锁定该位置的节点。

  ·在数组扩容的时候,则通过处理的步长和fwd节点来达到并发安全的目的,通过设置hash值为MOVED

  ·当把某个位置的节点复制到扩张后的table的时候,也通过Synchronized的同步机制来保证现程安全

 

 10、链表转为红黑树的过程 

   前面在讲解tryifyBin的源码的时候讲到过,如果在当个bin上的元素超过了8个的时候,就会尝试去扩容数组或者是将链表转为红黑树。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值