ConcurrentHashMap 是 HashMap 的线程安全版本,与之前版本的ConcurrentHashMap实现来看,java 8中做了较大调整,本文仅分析java 8的实现,java 8 之前的实现暂不做分析。

简单介绍

为了更好的导入本文,首先展示一下ConcurrentHashMap的结构,请看下面的图片:

和 HashMap 一样,ConcurrentHashMap 使用了一个 table 来存储 Node,ConcurrentHashMap 同样使用记录的 key 的 hashCode来寻找记录的存储 index,而处理哈希冲突的方式与 HashMap 也是类似的,冲突的记录将被存储在同一位置上,形成一条链表,当链表长度大于 8 时,会将链表转化为红黑树。从而将查找的复杂度从 O(N) 降到 O(logN)。

ConcurrentHashMap 怎么保证线程安全?

我们通过 ConcurrentHashMap 的 put 方法来讲解 它怎么实现线程安全。

put 方法实现步骤:

  1. 计算记录的 key 的 hashCode ,然后计算 table 的 index 位置,获取该 index 的值 x;
  2. 如果 x 为 null,说明还没有记录,调用 CAS 操作 该新的记录插入到table的index位置上去;
  3. 如果 x 不为 null,通过 synchronized 关键字 对 table 的 index 位置加锁;
  4. 然后判断table的index位置上的第一个节点的hashCode值,如果hashCode值小于0,那么就是一颗红黑树,如果不小于0,那么就还是一条链表;
  5. 如果是一条链表,查找链表寻找是否有一个记录的 key 值和本次插入的 key 值相同,相同则将替换掉 value 值;不同的话直接添加到链表中;
  6. 如果是一棵红黑树,调用 putTreeVal方法进行插入操作;
  7. 插入完成,判断是不是更新操作,不是更新操作的话,size + 1。

注意

  • 第 2 步中,CAS操作 即compare and swap,是非阻塞的原子性操作,由UnSafe类提供。
  • 第 3 步中,当前线程只会锁住table的index位置,其他位置上没有锁住,所以此时其他线程可以安全的获得其他的table位置来进行操作。这也就提高了ConcurrentHashMap的并发度。
  • 第 7 步中,table的扩容操作也会在更新size的时候发生,如果在更新size之后发现table中的记录数量达到了阈值,就需要进行扩容操作。

总结:

Java8 之后,ConcurrentHashMap 底层数据结构跟 HashMap 类似,都是基于 数组+链表+红黑树实现的,而 ConcurrentHashMap 之所以是线程安全的,是因为它抛弃了 Hashtable 给整个方法加 synchronized 锁的理念,而是让 CAS操作 和 synchronized锁 结合,在进行修改操作是,只对 table数组 的一项进行加锁,其他数组项可以继续被其他线程使用,这就提高了 ConcurrentHashMap 的并发度。
--------------------------------------------- 想看 put方法 的源码?请往下看 ---------------------------------------------

final V putVal(K key, V value, boolean onlyIfAbsent) {if (key == null || value == null) throw new NullPointerException();//ConcurrentHashMap不存储key/value为nullint hash = spread(key.hashCode());//计算key的hash值int binCount = 0;//桶中元素的大小,如果大于等于8,则旋转为红黑树for (Node<K,V>[] tab = table;;) {Node<K,V> f; int n, i, fh;//f :计算出key在 hash中数组桶的链表/红黑树的根,n:桶的长度,i:key在数组的位置,fh:f的hash值if (tab == null || (n = tab.length) == 0)tab = initTable();//如果数组为0或者没有元素,初始化else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//f指向数组中的值(链表/红黑树)if (casTabAt(tab, i, null,new Node<K,V>(hash, key, value, null)))//如果f为 null表示链表没有值,则此次放入的key/value存入链表的根 (1)break;                   // no lock when adding to empty bin}else if ((fh = f.hash) == MOVED)//如果在进行扩容先进行扩容tab = helpTransfer(tab, f);else {V oldVal = null;synchronized (f) {//此次添加key/value的链表有元素,则对链表/红黑树进行加锁 这里是java8和java7的不同之处 java8采用的加锁桶,而不是一段桶if (tabAt(tab, i) == f) {//出现hash碰撞(情况1:线程1和线程2同时插入在上面(1) 由于是CAS操作只有一个线程会成功,第二个线程会进入到这一步 情况2:普通的hash碰撞),if (fh >= 0) {//大于0表示桶是链表 TREEBIN   = -2 桶是红黑树binCount = 1;for (Node<K,V> e = f;; ++binCount) {K ek;//链表中的一个元素的keyif (e.hash == hash &&((ek = e.key) == key ||(ek != null && key.equals(ek)))) {//如何hash和key都和已存在的元素相等则根据onlyIfAbsebt的值,确定是用之前的值还是新值覆盖oldVal = e.val;if (!onlyIfAbsent)//如果onlyIfAbsent为fasle,新值覆盖老值e.val = value;break;//退出,操作完成}Node<K,V> pred = e;//链表最末尾的值作为新值的前一个元素if ((e = e.next) == null) {//如果已经到了末尾值,则创建新的node存放此次插入的key/valuepred.next = new Node<K,V>(hash, key,value, null);break;}}}else if (f instanceof TreeBin) {//如果节点为红黑树做红黑树的插入Node<K,V> p;binCount = 2;if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,value)) != null) {oldVal = p.val;if (!onlyIfAbsent)p.val = value;}}}}if (binCount != 0) {//如果不等于0判断是否需要旋转为红黑树if (binCount >= TREEIFY_THRESHOLD)//如果大于8则旋转为红黑树treeifyBin(tab, i);//旋转为红黑树if (oldVal != null)return oldVal;break;}}}addCount(1L, binCount);return null;
}

明天的你,一定会感谢今天努力的自己!

ConcurrentHashMap 底层原理,你真的理解了吗?相关推荐

  1. 并发编程三:深入理解并发List、Set、ConcurrentHashMap底层原理

    深入理解并发List.Set.ConcurrentHashMap底层原理 之前两篇分析了并发的三大特性和JMM模型,从硬件.jvm.java层面分别进行分析.JMM模型是并发当中最难理解的部分,涉及到 ...

  2. 嘿嘿,我就知道面试官接下来要问我 ConcurrentHashMap 底层原理了,看我怎么秀他...

    来自:烟雨星空 前言 上篇文章介绍了 HashMap 源码后,在博客平台广受好评,让本来己经不打算更新这个系列的我,仿佛被打了一顿鸡血.真的,被读者认可的感觉,就是这么奇妙. 原文:面试官再问你 Ha ...

  3. ConcurrentHashMap底层原理?

    本文为面试必备系列篇,不深入叙述,具体细节可自行查询. 可能会问的问题 1.用过ConcurrentHashMap吗? 2.为什么要用ConcurrentHashMap? 3.HashMap与Hash ...

  4. 深入浅入 ~ ConCurrentHashMap底层原理透析

    创作宗旨:化繁为简,绝不冗余,点到为止 ConcurrentHashMap<K,V> 继承了AbstractMap<K,V>,实现了ConcurrentMap<K,V&g ...

  5. 深入理解计算机原理与编译原理,【底层原理:深入理解计算机系统】#1 一切从'hello world'说起 (一)...

    计算机系统是由硬件和系统软件组成的,他们共同工作来运行应用程序.虽然系统的具体实现方式随着时间不断的在变化,但是系统的内在概念却没有改变的. 所有的计算机硬件和软件有着相似的结构和功能.这个系列专题便 ...

  6. HashMap的底层原理你真的知道?

    HashMap的底层实现原理是面试中出现频率非常高的一道面试题,本文将对HashMap的底层实现原理做一个简要的概况和总结,便于复习. 一.对于Map集合存储结构的理解 首先介绍以HashMap为典型 ...

  7. JS函数简单的底层原理 -变量重复声明无效,隐式申明,变量提升,函数提升,以及堆栈内存的变化

    JS函数简单的底层原理 (个人理解): 1. 已经使用var申明且赋值,若再次申明,则第二次申明(不赋值)无效. 2.在同一个作用域下,只要是发生了同名,且变量完成赋值,后者会覆盖前者.存在两个相同的 ...

  8. Java集合框架底层原理

    Java集合框架 Java集合框架 List集合 ArrayList底层实现原理 ArrayList数组扩容技术(数组拷贝) 扩容大小 查询和删除 集合中的泛型 LinkedList Vector 线 ...

  9. Java集合,ConcurrentHashMap底层实现和原理(常用于并发编程)

    为什么80%的码农都做不了架构师?>>>    概述 ConcurrentHashMap常用于并发编程,这里就从源码上来分析一下ConcurrentHashMap数据结构和底层原理. ...

最新文章

  1. 人脸识别虽好,还需行业自律与法律监管有道
  2. 关于异常处理的解决方案
  3. php7的foreach遍历数组,PHP中使用foreach遍历三维数组
  4. 系统、网络运维好帮手:expect
  5. android jni调用so库
  6. 华为鸿蒙os生态,华为鸿蒙系统终于来了! 首款方舟编译器应用正式上架: 鸿蒙OS可用...
  7. sybase 事务 超时返回_事务背景介绍(2):MongoDB中的逻辑会话
  8. Android OTA升级(2):开启升级过程
  9. 灵活使用示波器触发功能,帮助大大提高测量效率
  10. 将集合类转换成DataTable
  11. 6月29 Electron的第一课
  12. 计算机网络安全 第一章绪论
  13. 计算机原理 做实验报告,微机原理实验报告心得体会
  14. Java根据isbn查询图书信息_isbn书号查询api,根据图书ISBN查询详细信息
  15. C# 程序开机自动启动
  16. 用正则表达式进行身份证验证
  17. 计算机系统结构 第四章 指令级并行
  18. 如何(不)让你的Python代码不再晦涩难懂
  19. Linux系统双显示器4K分辨率配置
  20. 一种计算标准差的高效方法:Welford迭代法

热门文章

  1. 第一篇 多线程笔试面试题汇总(转)
  2. 【CSAPP笔记】4. 汇编语言——基础知识
  3. mybatis的动态sql及模糊查询
  4. 如何查看某个查询用了多少TempDB空间
  5. bzoj 1858: [Scoi2010]序列操作
  6. [python网络编程]使用scapy修改源IP发送请求
  7. Linux第六次学习笔记
  8. 《Entity Framework 6 Recipes》中文翻译系列 (45) ------ 第八章 POCO之获取原始对象与手工同步对象图和变化跟踪器...
  9. 拼接字符串时的引号嵌套
  10. 服务器计时器、Windows 计时器和线程计时器