ConcurrentHashMap源码解读

ConcurrentHashMap JDK1.8的 数据结构: 数组 + 链表(红黑树)

线程安全:在进行一些列操作的时候,多个线程进行操作和单个线程操作的结果不一致;

hashmap 1.8中出现线程安全的场景:

hash 算法(初始化之后hashMap的数组长度为16,hash算法首先得到key的整型数,然后控制这个整型数在0-15之间)只用于计算 不会在多线程的情况下出现问题

Hashmap 1.8中出现线程不安全的场景:

一. 数组初始化  Node[] table = new Node[16];  new HashMap的时候不会初始化数组,在元素调用put的时候初始化数组

二. put操作(put元素时,判断key是否为null)

     (1)直接判断key是否为null

       

   (2) key 不为null  

      ①key相同 --> value进行覆盖操作

      ② key不相同(hash 相同) 链表(尾插)  链表长度大于8,存储结构转换成红黑树

      ③ 链表太长转换红黑树的存储

三. 扩容机制(整个数据结构中Node节点的数量超过 数组大小*0.75 时 触发扩容) 会有线程安全问题

  ① 仅仅下表有元素(newTab 会重新计算 数组下标的位置)

  ②下标有元素下面有链表

e.hash & oldCap == 0 只有链表的情况(扩容时)

链表分成两部分:一部分时按兵不动,第二部分是比原来的位置+oldCap(老数组的大小)

正常思路:e.hash & newCap-1 得到具体小标的位置

  ③下标有元素下面有红黑树

  

 

 

HashTable (synchronized )解决方案:(synchronized修饰)效率低

ConcurrentHashMap  线程安全的解决方案:

一.  数组初始化:compareAndSwapInt 使用CAS原理来保证线程安全。

二. put操作(put元素时,判断key是否为null)

(1) 利用tabAt(使用volatile)来确保拿到key的最新值,key为null时 再使用casTabAt的无锁化比较,进行赋值

以下情况利用synchronized加锁:

    ConcurrentHashMap 在key值冲突操作链表时会使用 synchronized 进行加锁(锁的是当前key下的链表) 原因:

    CAS在 并发操作下会出现不断的比较来判断是否可以获取锁而synchronized直接锁住对象;

①key相同 --> value进行覆盖操作

② key不相同(hash 相同) 链表(尾插)  链表长度大于8,存储结构转换成红黑树

③ 链表太长转换红黑树的存储

三. 扩容机制

put元素时扩容机制会在synchronized之外进行:

① 正常扩容(数组容量到达 数组长度*0.75):

扩容方法 : put --> putVal --> addCount --> transfer(tab, null)(扩容方法)

(1) 控制只有一个线程能够创建新的数组

      transfer的第二个参数为null时,表示当前线程初始化数组(进行扩容)

      在其他线程put时,判断MOVED 如果 = -1 会进行帮助扩容,transfer的第二个参数不为null

(2)多个线程同时进行搬移Node节点

put元素时判断是否在扩容,如果扩容就帮助它扩容

②  链表转红黑树:

   链表长度 大于 8时:

    

(1) 链表长度 大于8 但 数组长度 小于 64 出发扩容机制(调用 transfer) --> 单纯的对链表进行扩容  这样比转红黑树性能消耗低

   (2)     链表长度 大于8 但 数组长度 大于等于 64 转红黑树

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值