⑴背景

ConcurrentHashMap是线程安全高效的HashMap。而HashMap在多线程情况下强行使用HashMap的put方法可能会导致程序死循环,使CPU使用率达到100%。(http://firezhfox.iteye.com/blog/2241043),而使用HashTable效率不高,于是就出现了ConcurrentHashMap。

⑵HashTable与ConcurrentHashMap的区别

①HashTable容器使用synchronized来保证线程安全,但HashTable在线程竞争激烈情况下HashTable的效率非常低。因为当一个线程访问HashTable的同步方法,其他线程也访问HashTable的同步方法时,会进入轮询或者阻塞状态。

②ConcurrentHashMap并发访问效率高得益于锁分段技术,HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程都必须竞争同一把锁。而如果容器中有多把锁,每把锁都分别用于某一部分数据,那么当多线程访问容器中不同数据段数据时,线程间就不会存在锁定竞争,从而可以提高并发访问效率,ConcurrentHashMap的锁分段技术就是这个原理。 将数据分成一段一段地储存,然后每段数据配一把锁,当某一线访问其中一个数据段时,其他段的数据也能够被其他线程访问。

ConcurrentHashMap类图

ConcurrentHashMap结构图(锁分段)

ConcurrentHashMap是由Segment数组结构和HashEnrty数组结构组成。Segment是可重入锁,HashEntry用于存储键值对,HashEntry结构与HashMap中,类似就不重复说明了。

⑶源码解析

 /**
      * Returns the value to which the specified key is mapped,
      * or {@code null} if this map contains no mapping for the key.
      *
      * <p>More formally, if this map contains a mapping from a key
      * {@code k} to a value {@code v} such that {@code key.equals(k)},
      * then this method returns {@code v}; otherwise it returns
      * {@code null}.  (There can be at most one such mapping.)
      *
      * @throws NullPointerException if the specified key is null
      */
     public V get(Object key) {
         Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
         int h = spread(key.hashCode());
         if ((tab = table) != null && (n = tab.length) > 0 &&
             (e = tabAt(tab, (n - 1) & h)) != null) {
             if ((eh = e.hash) == h) {
                 if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                     return e.val;
             }
             else if (eh < 0)
                 return (p = e.find(h, key)) != null ? p.val : null;
             while ((e = e.next) != null) {
                 if (e.hash == h &&
                     ((ek = e.key) == key || (ek != null && key.equals(ek))))
                     return e.val;
             }
         }
         return null;
     }
 /** Implementation for put and putIfAbsent */
     final V putVal(K key, V value, boolean onlyIfAbsent) {
         if (key == null || value == null) throw new NullPointerException();
         int hash = spread(key.hashCode());
         int binCount = 0;
         for (Node<K,V>[] tab = table;;) {
             Node<K,V> f; int n, i, fh;
             if (tab == null || (n = tab.length) == 0)
                 tab = initTable();
             else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                 if (casTabAt(tab, i, null,
                              new Node<K,V>(hash, key, value, null)))
                     break;                   // no lock when adding to empty bin
             }
             else if ((fh = f.hash) == MOVED)
                 tab = helpTransfer(tab, f);
             else {
                 V oldVal = null;
                 synchronized (f) {
                     if (tabAt(tab, i) == f) {
                         if (fh >= 0) {
                             binCount = 1;
                             for (Node<K,V> e = f;; ++binCount) {
                                 K ek;
                                 if (e.hash == hash &&
                                     ((ek = e.key) == key ||
                                      (ek != null && key.equals(ek)))) {
                                     oldVal = e.val;
                                     if (!onlyIfAbsent)
                                         e.val = value;
                                     break;
                                 }
                                 Node<K,V> pred = e;
                                 if ((e = e.next) == null) {
                                     pred.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) {
                     if (binCount >= TREEIFY_THRESHOLD)
                         treeifyBin(tab, i);
                     if (oldVal != null)
                         return oldVal;
                     break;
                 }
             }
         }
         addCount(1L, binCount);
         return null;
     }

这篇博主写得不错

Java源码之ConcurrentHashMap的更多相关文章

  1. 【数据结构】12.java源码关于ConcurrentHashMap

    目录 1.ConcurrentMap的内部结构 2.ConcurrentMap构造函数 3.元素新增策略4.元素删除5.元素修改和查找6.特殊操作7.扩容8.总结 1.ConcurrentMap内部结 ...

  2. Java 源码刨析 - HashMap 底层实现原理是什么?JDK8 做了哪些优化?

    [基本结构] 在 JDK 1.7 中 HashMap 是以数组加链表的形式组成的: JDK 1.8 之后新增了红黑树的组成结构,当链表大于 8 并且容量大于 64 时,链表结构会转换成红黑树结构,它的 ...

  3. 如何阅读Java源码 阅读java的真实体会

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心.   说到技术基础,我打个比 ...

  4. Android反编译(一)之反编译JAVA源码

    Android反编译(一) 之反编译JAVA源码 [目录] 1.工具 2.反编译步骤 3.实例 4.装X技巧 1.工具 1).dex反编译JAR工具  dex2jar   http://code.go ...

  5. 如何阅读Java源码

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动.源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧, ...

  6. Java 源码学习线路————_先JDK工具包集合_再core包,也就是String、StringBuffer等_Java IO类库

    http://www.iteye.com/topic/1113732 原则网址 Java源码初接触 如果你进行过一年左右的开发,喜欢用eclipse的debug功能.好了,你现在就有阅读源码的技术基础 ...

  7. Programming a Spider in Java 源码帖

    Programming a Spider in Java 源码帖 Listing 1: Finding the bad links (CheckLinks.java) import java.awt. ...

  8. 解密随机数生成器(二)——从java源码看线性同余算法

    Random Java中的Random类生成的是伪随机数,使用的是48-bit的种子,然后调用一个linear congruential formula线性同余方程(Donald Knuth的编程艺术 ...

  9. Java--Eclipse关联Java源码

    打开Eclipse,Window->Preferences->Java 点Edit按钮后弹出: 点Source Attachment后弹出: 选择Java安装路径下的src.zip文件即可 ...

随机推荐

  1. MyBatis从入门到精通(十一):MyBatis高级结果映射之一对多映射

    最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 本篇博客主要讲解MyBatis中如何使 ...

  2. Java编程思想:进程控制

    import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public ...

  3. mac环境下java项目无创建文件的权限

    1.问题: 先抛问题,由于刚刚换用mac环境,之前windows上开发的代码调试完毕,还未上线.之后上线部署之前,tl直连测试本地环境(mac)环境,功能无法使用,显示java.io.IOExcept ...

  4. Spring Cloud Alibaba | Sentinel: 服务限流高级篇

    目录 Spring Cloud Alibaba | Sentinel: 服务限流高级篇 1. 熔断降级 1.1 降级策略 2. 热点参数限流 2.1 项目依赖 2.2 热点参数规则 3. 系统自适应限 ...

  5. 实验吧--web--你真的会php吗

    ---恢复内容开始--- 实验吧的一道题php审计题.拉下来写一写. http://ctf5.shiyanbar.com/web/PHP/index.php 打开之后说have fun 那就抓包来看看 ...

  6. C#4.0新增功能04 嵌入的互操作类型

    连载目录    [已更新最新开发文章,点击查看详细] 从 .NET Framework 4 开始,公共语言运行时支持将 COM 类型的类型信息直接嵌入到托管程序集中,而不要求托管程序集从互操作程序集中 ...

  7. 使用.net core基于Razor Pages开发网站一些工作笔记

    本文是在实践工作中遇到的一些问题记录,并给出是如何解决的,.net core已经升级到3.0版本了,其实在项目中很早就已经在使用.net core来开发后台接口了,正好有个网站项目,就使用了Razor ...

  8. typescript 公共,私有与受保护的修饰符

    public理解 当你在程序中没有指明修饰符时,默认为public,也就是在类内类外都可以访问,我们以下面的例子来解释. class Person{ name:string sex:string ag ...

  9. 自动装配、JavaConfig、XML 三种方案之间,怎么导入和混合配置?

    在 Spring 中,这些配置方案都不是互斥的.完全可以将 JavaConfig 的组件扫描和自动装配/或 XML 配置混合在一起. Q:如何在 JavaConfig 中引用 XML 配置? Q:怎么 ...

  10. [学习笔记] NumPy走一趟(持续更)

    Numpy学习笔记 之前没有花时间去专门学Numpy,都是用到什么就草草查一下,最近在学DeepLearning,就决定还是系统地把Numpy学一遍. 一.Numpy基础篇 https://www.r ...