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

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

注:在看这篇文章之前,如果对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、使用方法:

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

2.2、源代码:

ConcurrentHashMap相关属性:

    /*** 用于分段*/// 根据这个数来计算segment的个数,segment的个数是仅小于这个数且是2的几次方的一个数(ssize)static final int DEFAULT_CONCURRENCY_LEVEL = 16;// 最大的分段(segment)数(2的16次方)static final int MAX_SEGMENTS = 1 << 16;/*** 用于HashEntry*/// 默认的用于计算Segment数组中的每一个segment的HashEntry[]的容量,但是并不是每一个segment的HashEntry[]的容量static final int DEFAULT_INITIAL_CAPACITY = 16;// 默认的加载因子(用于resize)static final float DEFAULT_LOAD_FACTOR = 0.75f;// 用于计算Segment数组中的每一个segment的HashEntry[]的最大容量(2的30次方)static final int MAXIMUM_CAPACITY = 1 << 30;/*** segments数组* 每一个segment元素都看做是一个HashTable*/final Segment<K, V>[] segments;/*** 用于扩容*/final int segmentMask;// 用于根据给定的key的hash值定位到一个Segmentfinal int segmentShift;// 用于根据给定的key的hash值定位到一个Segment

Segment类(ConcurrentHashMap的内部类)

    /*** 一个特殊的HashTable*/static final class Segment<K, V> extends ReentrantLock implementsSerializable {private static final long serialVersionUID = 2249069246763182397L;transient volatile int count;// 该Segment中的包含的所有HashEntry中的key-value的个数transient int modCount;// 并发标记/** 元素个数超出了这个值就扩容 threshold==(int)(capacity * loadFactor)* 值得注意的是,只是当前的Segment扩容,所以这是Segment自己的一个变量,而不是ConcurrentHashMap的*/transient int threshold;transient volatile HashEntry<K, V>[] table;// 链表数组final float loadFactor;/*** 这里要注意一个很不好的编程习惯,就是小写l,容易与数字1混淆,所以最好不要用小写l,可以改为大写L*/Segment(int initialCapacity, float lf) {loadFactor = lf;//每个Segment的加载因子setTable(HashEntry.<K, V> newArray(initialCapacity));}/*** 创建一个Segment数组,容量为i*/@SuppressWarnings("unchecked")static final <K, V> Segment<K, V>[] newArray(int i) {return new Segment[i];}/*** Sets table to new HashEntry array. Call only while holding lock or in* constructor.*/void setTable(HashEntry<K, V>[] newTable) {threshold = (int) (newTable.length * loadFactor);// 设置扩容值table = newTable;// 设置链表数组}

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

HashEntry类(ConcurrentHashMap的内部类)

    /*** Segment中的HashEntry节点 类比HashMap中的Entry节点*/static final class HashEntry<K, V> {final K key;// 键final int hash;//hash值volatile V value;// 实现线程可见性final HashEntry<K, V> next;// 下一个HashEntryHashEntry(K key, int hash, HashEntry<K, V> next, V value) {this.key = key;this.hash = hash;this.next = next;this.value = value;}/** 创建HashEntry数组,容量为传入的i*/@SuppressWarnings("unchecked")static final <K, V> HashEntry<K, V>[] newArray(int i) {return new HashEntry[i];}}

ConcurrentHashMap(int initialCapacity,float loadFactor,int concurrencyLevel)

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

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

  • 每一个参数的作用

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

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

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

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

ConcurrentHashMap源码解析(1)相关推荐

  1. 面试官系统精讲Java源码及大厂真题 - 16 ConcurrentHashMap 源码解析和设计思路

    16 ConcurrentHashMap 源码解析和设计思路 与有肝胆人共事,从无字句处读书. 引导语 当我们碰到线程不安全场景下,需要使用 Map 的时候,我们第一个想到的 API 估计就是 Con ...

  2. ConcurrentHashMap源码解析——基于JDK1.8

    ConcurrentHashMap源码解析--基于JDK1.8 前言 这篇博客不知道写了多久,总之就是很久,头都炸了.最开始阅读源码时确实是一脸茫然,找不到下手的地方,真是太难了.下面的都是我自己阅读 ...

  3. ConcurrentHashMap源码解析_02 预热(内部一些小方法分析)

    前面一篇文章中介绍了并发HashMap的主要成员属性,内部类和构造函数,下面在正式分析并发HashMap成员方法之前,先分析一些内部类中的字方法函数: 首先来看下ConcurrentHashMap内部 ...

  4. ConcurrentHashMap源码解析_01 成员属性、内部类、构造方法分析

    文章参考:小刘源码 ConcurrentHashMap源码解析_01 成员属性.内部类.构造方法分析 1.简介 ConcurrentHashMap是HashMap的线程安全版本,内部也是使用(数组 + ...

  5. hashmap与concurrenthashmap源码解析

    hashmap源码解析转载:http://www.cnblogs.com/ITtangtang/p/3948406.html 一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此 ...

  6. JDK8中ConcurrentHashMap源码解析

    在介绍ConcurrentHashMap源码之前,首先需要了解以下几个知识 1.JDK1.8中ConcurrentHashMap的基本结构 2.并发编程的三个概念:可见性,原子性,有序性 3.CAS( ...

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

    Linux编程 点击右侧关注,免费入门到精通! 作者丨红橙Darren https://www.jianshu.com/p/0b452a6e4f4e 五个线程同时往 HashMap 中 put 数据会 ...

  8. JDK1.8 ConcurrentHashMap 源码解析

    概述 ConcurrentHashMap 是 util.concurrent 包的重要成员. ConcurrentHashMap 的源代码会涉及到散列算法,链表数据结构和红黑树 Java8 Concu ...

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

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

最新文章

  1. POJ_1195 Mobile phones 【二维树状数组】
  2. 《C#3.0 in a Nutshell ,3rd Edition》之序言篇
  3. Linux/UNIX网络编程之API
  4. boost::multiprecision模块hash相关的测试程序
  5. 项目中使用CLR编程
  6. 125条常见的java面试笔试题大汇总3
  7. 2018年最好用的百度网盘资源搜索神器排行
  8. 勒索病毒GandCrab5.2解密工具
  9. 1.49万件区块链专利全景:BATJP占26%,游戏类迎突破
  10. Nods.js安装配置(windows)
  11. vue项目文件命名规范推荐
  12. 什么是“光照度(Illuminance)”?
  13. 网络用语大扫盲?今天 你说了没?
  14. 部署Elixir项目
  15. 实训日志(十)——达芬奇调色
  16. 如何在 R 中计算条件概率
  17. 起源计划丨四大战队集结 向最后的荣耀冲刺
  18. Android开发中的Log打印日志
  19. 5W3H法与SMART原则的结合使用
  20. Java反射invoke报错wrong number of arguments

热门文章

  1. VB中对AutoCAD图形对象进行缩放操作
  2. select 语句的执行顺序
  3. Spark的RDD序列化
  4. 遍历目录寻找指定文件
  5. RocEDU.课程设计2018 第二周进展 博客补交
  6. day02 文件读写
  7. 【转载】Eclipse 最常用快捷键 (动画讲解),最简单的一些快捷键
  8. python --time()函数
  9. 团队-象棋游戏-模块开发过程
  10. 关于verilog中的signed类型