ConcurrentHashMap源码解析(1)
此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
注:在看这篇文章之前,如果对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)相关推荐
- 面试官系统精讲Java源码及大厂真题 - 16 ConcurrentHashMap 源码解析和设计思路
16 ConcurrentHashMap 源码解析和设计思路 与有肝胆人共事,从无字句处读书. 引导语 当我们碰到线程不安全场景下,需要使用 Map 的时候,我们第一个想到的 API 估计就是 Con ...
- ConcurrentHashMap源码解析——基于JDK1.8
ConcurrentHashMap源码解析--基于JDK1.8 前言 这篇博客不知道写了多久,总之就是很久,头都炸了.最开始阅读源码时确实是一脸茫然,找不到下手的地方,真是太难了.下面的都是我自己阅读 ...
- ConcurrentHashMap源码解析_02 预热(内部一些小方法分析)
前面一篇文章中介绍了并发HashMap的主要成员属性,内部类和构造函数,下面在正式分析并发HashMap成员方法之前,先分析一些内部类中的字方法函数: 首先来看下ConcurrentHashMap内部 ...
- ConcurrentHashMap源码解析_01 成员属性、内部类、构造方法分析
文章参考:小刘源码 ConcurrentHashMap源码解析_01 成员属性.内部类.构造方法分析 1.简介 ConcurrentHashMap是HashMap的线程安全版本,内部也是使用(数组 + ...
- hashmap与concurrenthashmap源码解析
hashmap源码解析转载:http://www.cnblogs.com/ITtangtang/p/3948406.html 一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此 ...
- JDK8中ConcurrentHashMap源码解析
在介绍ConcurrentHashMap源码之前,首先需要了解以下几个知识 1.JDK1.8中ConcurrentHashMap的基本结构 2.并发编程的三个概念:可见性,原子性,有序性 3.CAS( ...
- 数据结构算法 - ConcurrentHashMap 源码解析
Linux编程 点击右侧关注,免费入门到精通! 作者丨红橙Darren https://www.jianshu.com/p/0b452a6e4f4e 五个线程同时往 HashMap 中 put 数据会 ...
- JDK1.8 ConcurrentHashMap 源码解析
概述 ConcurrentHashMap 是 util.concurrent 包的重要成员. ConcurrentHashMap 的源代码会涉及到散列算法,链表数据结构和红黑树 Java8 Concu ...
- 并发编程(十六)——java7 深入并发包 ConcurrentHashMap 源码解析
以前写过介绍HashMap的文章,文中提到过HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容 ...
最新文章
- POJ_1195 Mobile phones 【二维树状数组】
- 《C#3.0 in a Nutshell ,3rd Edition》之序言篇
- Linux/UNIX网络编程之API
- boost::multiprecision模块hash相关的测试程序
- 项目中使用CLR编程
- 125条常见的java面试笔试题大汇总3
- 2018年最好用的百度网盘资源搜索神器排行
- 勒索病毒GandCrab5.2解密工具
- 1.49万件区块链专利全景:BATJP占26%,游戏类迎突破
- Nods.js安装配置(windows)
- vue项目文件命名规范推荐
- 什么是“光照度(Illuminance)”?
- 网络用语大扫盲?今天 你说了没?
- 部署Elixir项目
- 实训日志(十)——达芬奇调色
- 如何在 R 中计算条件概率
- 起源计划丨四大战队集结 向最后的荣耀冲刺
- Android开发中的Log打印日志
- 5W3H法与SMART原则的结合使用
- Java反射invoke报错wrong number of arguments