此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

注:在看这篇文章之前,如果对HashMap的层不清楚的话,建议先去看看HashMap源码解析。

http://www.cnblogs.com/java-zhao/p/5106189.html

1、对于ConcurrentHashMap需要掌握以下几点

  • Map的创建:ConcurrentHashMap()

  • 往Map中添加键值对:即put(Object key, Object value)方法

  • 获取Map中的单个对象:即get(Object key)方法

  • 删除Map中的对象:即remove(Object key)方法

  • 判断对象是否存在于Map中:containsKey(Object key)

  • 遍历Map中的对象:即keySet().iterator(),在实际中更常用的是增强型的for循环去做遍历

2、ConcurrentHashMap的创建

注:在往下看之前,心里先有这样一个映像:ConcurrentHashMap的数据结构:一个指定个数的Segment数组,数组中的每一个元素Segment相当于一个HashTable

2.1、使用方法:

  1. Map<String, Object> map = new ConcurrentHashMap<String, Object>();

2.2、源代码:

ConcurrentHashMap相关属性:

  1.     /**
  2.      * 用于分段
  3.      */
  4.     // 根据这个数来计算segment的个数,segment的个数是仅小于这个数且是2的几次方的一个数(ssize)
  5.     static final int DEFAULT_CONCURRENCY_LEVEL = 16;
  6.     // 最大的分段(segment)数(2的16次方)
  7.     static final int MAX_SEGMENTS = 1 << 16;
  8.     
  9.     /**
  10.      * 用于HashEntry
  11.      */
  12.     // 默认的用于计算Segment数组中的每一个segment的HashEntry[]的容量,但是并不是每一个segment的HashEntry[]的容量
  13.     static final int DEFAULT_INITIAL_CAPACITY = 16;
  14.     // 默认的加载因子(用于resize)
  15.     static final float DEFAULT_LOAD_FACTOR = 0.75f;
  16.     // 用于计算Segment数组中的每一个segment的HashEntry[]的最大容量(2的30次方)
  17.     static final int MAXIMUM_CAPACITY = 1 << 30;
  18.  
  19.     /**
  20.      * segments数组
  21.      * 每一个segment元素都看做是一个HashTable
  22.      */
  23.     final Segment<K, V>[] segments;
  24.     
  25.     /**
  26.      * 用于扩容
  27.      */
  28.     final int segmentMask;// 用于根据给定的key的hash值定位到一个Segment
  29.     final int segmentShift;// 用于根据给定的key的hash值定位到一个Segment

Segment类(ConcurrentHashMap的内部类)

  1.     /**
  2.      * 一个特殊的HashTable
  3.      */
  4.     static final class Segment<K, V> extends ReentrantLock implements
  5.             Serializable {
  6.  
  7.         private static final long serialVersionUID = 2249069246763182397L;
  8.  
  9.         transient volatile int count;// 该Segment中的包含的所有HashEntry中的key-value的个数
  10.         transient int modCount;// 并发标记
  11.  
  12.         /*
  13.          * 元素个数超出了这个值就扩容 threshold==(int)(capacity * loadFactor)
  14.          * 值得注意的是,只是当前的Segment扩容,所以这是Segment自己的一个变量,而不是ConcurrentHashMap的
  15.          */
  16.         transient int threshold;
  17.         transient volatile HashEntry<K, V>[] table;// 链表数组
  18.         final float loadFactor;
  19.  
  20.         /**
  21.          * 这里要注意一个很不好的编程习惯,就是小写l,容易与数字1混淆,所以最好不要用小写l,可以改为大写L
  22.          */
  23.         Segment(int initialCapacity, float lf) {
  24.             loadFactor = lf;//每个Segment的加载因子
  25.             setTable(HashEntry.<K, V> newArray(initialCapacity));
  26.         }
  27.  
  28.         /**
  29.          * 创建一个Segment数组,容量为i
  30.          */
  31.         @SuppressWarnings("unchecked")
  32.         static final <K, V> Segment<K, V>[] newArray(int i) {
  33.             return new Segment[i];
  34.         }
  35.  
  36.         /**
  37.          * Sets table to new HashEntry array. Call only while holding lock or in
  38.          * constructor.
  39.          */
  40.         void setTable(HashEntry<K, V>[] newTable) {
  41.             threshold = (int) (newTable.length * loadFactor);// 设置扩容值
  42.             table = newTable;// 设置链表数组
  43.         }

说明:只列出了Segement的全部属性和创建ConcurrentHashMap时所用到的方法。

HashEntry类(ConcurrentHashMap的内部类)

  1.     /**
  2.      * Segment中的HashEntry节点 类比HashMap中的Entry节点
  3.      */
  4.     static final class HashEntry<K, V> {
  5.         final K key;// 键
  6.         final int hash;//hash值
  7.         volatile V value;// 实现线程可见性
  8.         final HashEntry<K, V> next;// 下一个HashEntry
  9.  
  10.         HashEntry(K key, int hash, HashEntry<K, V> next, V value) {
  11.             this.key = key;
  12.             this.hash = hash;
  13.             this.next = next;
  14.             this.value = value;
  15.         }
  16.  
  17.         /*
  18.          * 创建HashEntry数组,容量为传入的i
  19.          */
  20.         @SuppressWarnings("unchecked")
  21.         static final <K, V> HashEntry<K, V>[] newArray(int i) {
  22.             return new HashEntry[i];
  23.         }
  24.     }

ConcurrentHashMap(int initialCapacity,float loadFactor,int concurrencyLevel)

  1.  1     /**
  2.  2      * 创建ConcurrentHashMap
  3.  3      * @param initialCapacity 用于计算Segment数组中的每一个segment的HashEntry[]的容量, 但是并不是每一个segment的HashEntry[]的容量
  4.  4      * @param loadFactor
  5.  5      * @param concurrencyLevel 用于计算Segment数组的大小(可以传入不是2的几次方的数,但是根据下边的计算,最终segment数组的大小ssize将是2的几次方的数)
  6.  6      * 
  7.  7      * 步骤:
  8.  8      * 这里以默认的无参构造器参数为例,initialCapacity==16,loadFactor==0.75f,concurrencyLevel==16
  9.  9      * 1)检查各参数是否符合要求
  10. 10      * 2)根据concurrencyLevel(16),计算Segment[]的容量ssize(16)与扩容移位条件sshift(4)
  11. 11      * 3)根据sshift与ssize计算将来用于定位到相应Segment的参数segmentShift与segmentMask
  12. 12      * 4)根据ssize创建Segment[]数组,容量为ssize(16)
  13. 13      * 5)根据initialCapacity(16)与ssize计算用于计算HashEntry[]容量的参数c(1)
  14. 14      * 6)根据c计算HashEntry[]的容量cap(1)
  15. 15      * 7)根据cap与loadFactor(0.75)为每一个Segment[i]都实例化一个Segment
  16. 16      * 8)每一个Segment的实例化都做下面这些事儿:
  17. 17      * 8.1)为当前的Segment初始化其loadFactor为传入的loadFactor(0.75)
  18. 18      * 8.2)创建一个HashEntry[],容量为传入的cap(1)
  19. 19      * 8.3)根据创建出来的HashEntry的容量(1)和初始化的loadFactor(0.75),计算扩容因子threshold(0)
  20. 20      * 8.4)初始化Segment的table为刚刚创建出来的HashEntry
  21. 21      */
  22. 22     public ConcurrentHashMap(int initialCapacity,float loadFactor,int concurrencyLevel) {
  23. 23         // 检查参数情况
  24. 24         if (loadFactor <= 0f || initialCapacity < 0 || concurrencyLevel <= 0)
  25. 25             throw new IllegalArgumentException();
  26. 26 
  27. 27         if (concurrencyLevel > MAX_SEGMENTS)
  28. 28             concurrencyLevel = MAX_SEGMENTS;
  29. 29 
  30. 30         /**
  31. 31          * 找一个能够正好小于concurrencyLevel的数(这个数必须是2的几次方的数)
  32. 32          * eg.concurrencyLevel==16==>sshift==4,ssize==16
  33. 33          * 当然,如果concurrencyLevel==15也是上边这个结果
  34. 34          */
  35. 35         int sshift = 0;
  36. 36         int ssize = 1;// segment数组的长度
  37. 37         while (ssize < concurrencyLevel) {
  38. 38             ++sshift;
  39. 39             ssize <<= 1;// ssize=ssize*2
  40. 40         }
  41. 41 
  42. 42         segmentShift = 32 - sshift;// eg.segmentShift==32-4=28 用于根据给定的key的hash值定位到一个Segment
  43. 43         segmentMask = ssize - 1;// eg.segmentMask==16-1==15 用于根据给定的key的hash值定位到一个Segment
  44. 44         this.segments = Segment.newArray(ssize);// 构造出了Segment[ssize]数组 eg.Segment[16]
  45. 45 
  46. 46         /*
  47. 47          * 下面将为segment数组中添加Segment元素
  48. 48          */
  49. 49         if (initialCapacity > MAXIMUM_CAPACITY)
  50. 50             initialCapacity = MAXIMUM_CAPACITY;
  51. 51         int c = initialCapacity / ssize;// eg.initialCapacity==16,c==16/16==1
  52. 52         if (* ssize < initialCapacity)// eg.initialCapacity==17,c==17/16=1,这时1*16<17,所以c=c+1==2
  53. 53             ++c;// 为了少执行这一句,最好将initialCapacity设置为2的几次方
  54. 54         int cap = 1;// 每一个Segment中的HashEntry[]的初始化容量
  55. 55         while (cap < c)
  56. 56             cap <<= 1;// 创建容量
  57. 57 
  58. 58         for (int i = 0; i < this.segments.length; ++i)
  59. 59             // 这一块this.segments.length就是ssize,为了不去计算这个值,可以直接改成i<ssize
  60. 60             this.segments[i] = new Segment<K, V>(cap, loadFactor);
  61. 61     }

注意:这个方法里边我在头部所写的注释非常重要,在这块注释写明了:

  • 每一个参数的作用

  • 整个ConcurrentHashMap的一个创建步骤(以默认的参数值为例)

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 FUI-我离钢铁侠还差几步?
【推荐】 网易云数据库架构设计实践
【推荐】 云计算交互设计师的正确出装姿势

ConcurrentHashMap源码解析(1)的更多相关文章

  1. Java之ConcurrentHashMap源码解析

    ConcurrentHashMap源码解析 目录 ConcurrentHashMap源码解析 jdk8之前的实现原理 jdk8的实现原理 变量解释 初始化 初始化table put操作 hash算法 ...

  2. 第二章 ConcurrentHashMap源码解析

    注:在看这篇文章之前,如果对HashMap的层不清楚的话,建议先去看看HashMap源码解析. http://www.cnblogs.com/java-zhao/p/5106189.html 1.对于 ...

  3. ConcurrentHashMap源码解析,多线程扩容

    前面一篇已经介绍过了 HashMap 的源码: HashMap源码解析.jdk7和8之后的区别.相关问题分析 HashMap并不是线程安全的,他就一个普通的容器,没有做相关的同步处理,因此线程不安全主 ...

  4. Java并发包源码学习系列:JDK1.8的ConcurrentHashMap源码解析

    目录 为什么要使用ConcurrentHashMap? ConcurrentHashMap的结构特点 Java8之前 Java8之后 基本常量 重要成员变量 构造方法 tableSizeFor put ...

  5. 数据结构算法 - ConcurrentHashMap 源码解析

    五个线程同时往 HashMap 中 put 数据会发生什么? ConcurrentHashMap 是怎么保证线程安全的? 在分析 HashMap 源码时还遗留这两个问题,这次我们站在 Java 多线程 ...

  6. 并发编程(十六)——java7 深入并发包 ConcurrentHashMap 源码解析

    以前写过介绍HashMap的文章,文中提到过HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容 ...

  7. 深入并发包 ConcurrentHashMap 源码解析

    以前写过介绍HashMap的文章,文中提到过HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容 ...

  8. ConcurrentHashMap源码解析 JDK8

    一.简介 上篇文章详细介绍了HashMap的源码及原理,本文趁热打铁继续分析ConcurrentHashMap的原理. 首先在看本文之前,希望对HashMap有一个详细的了解.不然看直接看Concur ...

  9. ConcurrentHashMap源码解析-Java7

    目录 一.ConcurrentHashMap的模型图 二.源码分析-类定义 2.1 极简ConcurrentHashMap定义 2.2 Segment内部类 2.3 HashEntry内部类 2.4 ...

随机推荐

  1. centos 6.5 搭建zookeeper集群

    为什么使用Zookeeper? 大部分分布式应用需要一个主控.协调器或控制器来管理物理分布的子进程(如资源.任务分配等)目前,大部分应用需要开发私有的协调程序,缺乏一个通用的机制协调程序的反复编写浪费 ...

  2. delphi 中如何处理“幽灵”、“熔断”?(转载)

    原始连接:http://dannywind.nl/delphi/meltdown-spectre-and-delphi/ Meltdown, Spectre and Delphi Don’t pani ...

  3. php中如何配置项目虚拟路径

    php虚拟目录的设置在apache目录下打开conf->httpd.conf文件,找到<IfModule dir_module>,在</IfModule>后面添加如下代码 ...

  4. 2018.11.02 NOIP训练 停车场(线段树)

    传送门 这是一道困饶了我一年的题. 其实就是去年去NOIP提高组试水的时候考的模拟题 但当时我水平不够,跟ykykyk一起杠了一个下午都没调出来. 今天终于AAA了. 其实就是一个维护最长连续0101 ...

  5. 使用 docker compose 安装 tidb

    目标 : 单机上通过 Docker Compose 快速一键部署一套 TiDB 测试集群 前提条件: 1.centos版本在7.3 以上 2.安装git 3.安装docker Docker versi ...

  6. Le Chapitre X

    Il se trouvait dans la région des astéroïdes 325, 326, 327, 328, 329 et 330. Il commença donc par le ...

  7. Codeforces Round #543 (Div. 2)B,C

    https://codeforces.com/contest/1121 B 题意 给你n(<=1000)个数ai,找出最多对和相等的数,每个数只能用一次,且每个数保证各不相同 题解 重点:每个数 ...

  8. UVa 10294 Arif in Dhaka (First Love Part 2) (Polya定理)

    题意:给定 n 和 m 表示要制作一个项链和手镯,项链和手镯的区别就是手镯旋转和翻转都是相同的,而项链旋转都是相同的,而翻转是不同的,问你使用 n 个珠子和 m 种颜色可以制作多少种项链和手镯. 析: ...

  9. MarkDown总结

    转载自:https://www.jianshu.com/p/q81RER:做了一些部分修改: 一:总纲 1.所有的格式均要空格,如#后面加个空格,-后面加空格,1.后面加空格等等: 2.双向包裹的不能 ...

  10. Windows 下 Quartus 检测不到 USB-Blaster 终极解决办法

    转自https://blog.csdn.net/acang301/article/details/50471067?locationNum=12 一.Windows无法正常驱动USB-Blaster ...