文章目录

  • 注释
  • 类的继承与实现
  • 数据的存储
  • 构造函数
  • 哈希
  • put
  • get
  • 扩容

本系列是Java详解,专栏地址:Java源码分析


ConcurrentHashMap 官方文档:ConcurrentHashMap (Java Platform SE 8 )
ConcurrentHashMap.java源码共6383行,下载地址见我的文章:Java源码下载和阅读(JDK1.8/Java 11)

文件地址:openjdk-jdk11u-jdk-11.0.6-3/src/java.base/share/classes/java/concurrent


背景介绍:在看源码前,先说一下为什么要看源码。因为HashMap不是线程安全的,想要线程安全的话,需要使用ConcurrentHashMap。而Hashtable 本身比较低效,因为它的实现基本就是将 put、get、size 等各种方法加上“synchronized”。

注释

看源码的第一步是看代码开始前的注释,注释里提到几个点:
1.ConcurrentHashMap支持并发读写,类似Hashtable,区别在于访问时不会完全上锁。
2.get操作通常不会阻塞,因此可能同时发生put操作。get操作返回的结果是最近已经完成操作的结果,根据happens-before规则。
3.迭代器反映的是创建迭代器后某个时刻的状态,不会抛出ConcurrentModificationException异常。但迭代器只能被一个线程使用。
4.如果有很多冲突,那么会进行自动扩容,预期是每个map维护2个bin。resize比较慢,推荐初始化的时候设置好initialCapacityloadFactor
5.如果有很多key有相同的hashCode,会降低map的性能。改善的方法是让keyComparable
6.跟Hashtable一样不允许null的key或value。

类的继承与实现

public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>implements ConcurrentMap<K,V>, Serializable {

数据的存储

    transient volatile Node<K,V>[] table;private transient volatile int sizeCtl;private final Node<K,V>[] initTable() {Node<K,V>[] tab; int sc;while ((tab = table) == null || tab.length == 0) {if ((sc = sizeCtl) < 0)Thread.yield(); // lost initialization race; just spinelse if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {try {if ((tab = table) == null || tab.length == 0) {int n = (sc > 0) ? sc : DEFAULT_CAPACITY;@SuppressWarnings("unchecked")Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];table = tab = nt;sc = n - (n >>> 2);}} finally {sizeCtl = sc;}break;}}return tab;}

实行懒加载,在第一次put的时候进行数组的初始化。
变量sizeCtl防止多个线程同时进行数组的resize

构造函数

    public ConcurrentHashMap() {}public ConcurrentHashMap(int initialCapacity) {this(initialCapacity, LOAD_FACTOR, 1);}public ConcurrentHashMap(int initialCapacity, float loadFactor) {this(initialCapacity, loadFactor, 1);}public ConcurrentHashMap(int initialCapacity,float loadFactor, int concurrencyLevel) {if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)throw new IllegalArgumentException();if (initialCapacity < concurrencyLevel)   // Use at least as many binsinitialCapacity = concurrencyLevel;   // as estimated threadslong size = (long)(1.0 + (long)initialCapacity / loadFactor);int cap = (size >= (long)MAXIMUM_CAPACITY) ?MAXIMUM_CAPACITY : tableSizeFor((int)size);this.sizeCtl = cap;}

这里有个参数concurrencyLevel,意思是预计的对Map操作的并发数。

哈希

    static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hashstatic final int spread(int h) {return (h ^ (h >>> 16)) & HASH_BITS;}int hash = spread(key.hashCode());

HashMap的哈希一样,右移后取正数部分。

put

    public V put(K key, V value) {return putVal(key, value, false);}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; K fk; V fv;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)))break;                   // no lock when adding to empty bin}else if ((fh = f.hash) == MOVED)tab = helpTransfer(tab, f);else if (onlyIfAbsent // check first node without acquiring lock&& fh == hash&& ((fk = f.key) == key || (fk != null && key.equals(fk)))&& (fv = f.val) != null)return fv;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);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;}}else if (f instanceof ReservationNode)throw new IllegalStateException("Recursive update");}}if (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);if (oldVal != null)return oldVal;break;}}}addCount(1L, binCount);return null;}

具体的做法跟HashMap类似,但多了一个加锁的操作,在进行put前需要把桶中的第一个元素锁上,防止多线程put引起冲突。

get

    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;}

没有什么可说的。

扩容

扩容可以说是ConcurrentHashMap中最有技术含量的一个点。

    private final void addCount(long x, int check) {CounterCell[] cs; long b, s;if ((cs = counterCells) != null ||!U.compareAndSetLong(this, BASECOUNT, b = baseCount, s = b + x)) {CounterCell c; long v; int m;boolean uncontended = true;if (cs == null || (m = cs.length - 1) < 0 ||(c = cs[ThreadLocalRandom.getProbe() & m]) == null ||!(uncontended =U.compareAndSetLong(c, CELLVALUE, v = c.value, v + x))) {fullAddCount(x, uncontended);return;}if (check <= 1)return;s = sumCount();}if (check >= 0) {Node<K,V>[] tab, nt; int n, sc;while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&(n = tab.length) < MAXIMUM_CAPACITY) {int rs = resizeStamp(n);if (sc < 0) {if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||transferIndex <= 0)break;if (U.compareAndSetInt(this, SIZECTL, sc, sc + 1))transfer(tab, nt);}else if (U.compareAndSetInt(this, SIZECTL, sc,(rs << RESIZE_STAMP_SHIFT) + 2))transfer(tab, null);s = sumCount();}}}private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {int n = tab.length, stride;if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)stride = MIN_TRANSFER_STRIDE; // subdivide rangeif (nextTab == null) {            // initiatingtry {@SuppressWarnings("unchecked")Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];nextTab = nt;} catch (Throwable ex) {      // try to cope with OOMEsizeCtl = Integer.MAX_VALUE;return;}nextTable = nextTab;transferIndex = n;}int nextn = nextTab.length;ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);boolean advance = true;boolean finishing = false; // to ensure sweep before committing nextTabfor (int i = 0, bound = 0;;) {Node<K,V> f; int fh;while (advance) {int nextIndex, nextBound;if (--i >= bound || finishing)advance = false;else if ((nextIndex = transferIndex) <= 0) {i = -1;advance = false;}else if (U.compareAndSetInt(this, TRANSFERINDEX, nextIndex,nextBound = (nextIndex > stride ?nextIndex - stride : 0))) {bound = nextBound;i = nextIndex - 1;advance = false;}}if (i < 0 || i >= n || i + n >= nextn) {int sc;if (finishing) {nextTable = null;table = nextTab;sizeCtl = (n << 1) - (n >>> 1);return;}if (U.compareAndSetInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)return;finishing = advance = true;i = n; // recheck before commit}}else if ((f = tabAt(tab, i)) == null)advance = casTabAt(tab, i, null, fwd);else if ((fh = f.hash) == MOVED)advance = true; // already processedelse {synchronized (f) {if (tabAt(tab, i) == f) {Node<K,V> ln, hn;if (fh >= 0) {int runBit = fh & n;Node<K,V> lastRun = f;for (Node<K,V> p = f.next; p != null; p = p.next) {int b = p.hash & n;if (b != runBit) {runBit = b;lastRun = p;}}if (runBit == 0) {ln = lastRun;hn = null;}else {hn = lastRun;ln = null;}for (Node<K,V> p = f; p != lastRun; p = p.next) {int ph = p.hash; K pk = p.key; V pv = p.val;if ((ph & n) == 0)ln = new Node<K,V>(ph, pk, pv, ln);elsehn = new Node<K,V>(ph, pk, pv, hn);}setTabAt(nextTab, i, ln);setTabAt(nextTab, i + n, hn);setTabAt(tab, i, fwd);advance = true;}else if (f instanceof TreeBin) {TreeBin<K,V> t = (TreeBin<K,V>)f;TreeNode<K,V> lo = null, loTail = null;TreeNode<K,V> hi = null, hiTail = null;int lc = 0, hc = 0;for (Node<K,V> e = t.first; e != null; e = e.next) {int h = e.hash;TreeNode<K,V> p = new TreeNode<K,V>(h, e.key, e.val, null, null);if ((h & n) == 0) {if ((p.prev = loTail) == null)lo = p;elseloTail.next = p;loTail = p;++lc;}else {if ((p.prev = hiTail) == null)hi = p;elsehiTail.next = p;hiTail = p;++hc;}}ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :(hc != 0) ? new TreeBin<K,V>(lo) : t;hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :(lc != 0) ? new TreeBin<K,V>(hi) : t;setTabAt(nextTab, i, ln);setTabAt(nextTab, i + n, hn);setTabAt(tab, i, fwd);advance = true;}}}}}}

参考:

  • java - What’s the difference between ConcurrentHashMap and Collections.synchronizedMap(Map)? - Stack Overflow

Java源码详解六:ConcurrentHashMap源码分析--openjdk java 11源码相关推荐

  1. Rocksdb Compaction 源码详解(一):SST文件详细格式源码解析

    文章目录 前言 comapction流程概述 SST 文件细节 Footer meta index block filter meta block index meta block Compressi ...

  2. java多线程详解 六_java多线程学习-java.util.concurrent详解(六) Exchanger

    转载于:http://janeky.iteye.com/blog/769965 我们先来学习一下JDK1.5 API中关于这个类的详细介绍: "可以在pair中对元素进行配对和交换的线程的同 ...

  3. Rocksdb Compaction源码详解(二):Compaction 完整实现过程 概览

    文章目录 1. 摘要 2. Compaction 概述 3. 实现 3.1 Prepare keys 过程 3.1.1 compaction触发的条件 3.1.2 compaction 的文件筛选过程 ...

  4. 4.6 W 字总结!Java 11—Java 17特性详解

    作者 | 民工哥技术之路 来源 | https://mp.weixin.qq.com/s/SVleHYFQeePNT7q67UoL4Q Java 11 特性详解 基于嵌套的访问控制 与 Java 语言 ...

  5. java异常体系结构详解

    java异常体系结构详解 参考文章: (1)java异常体系结构详解 (2)https://www.cnblogs.com/hainange/p/6334042.html 备忘一下.

  6. java异常处理机制详解

    java异常处理机制详解 参考文章: (1)java异常处理机制详解 (2)https://www.cnblogs.com/vaejava/articles/6668809.html 备忘一下.

  7. java多线程学习-java.util.concurrent详解

    http://janeky.iteye.com/category/124727 java多线程学习-java.util.concurrent详解(一) Latch/Barrier 博客分类: java ...

  8. java nio详解,Java NIO API详解

    Java NIO API详解 在JDK 1.4以前,Java的IO操作集中在java.io这个包中,是基于流的阻塞(blocking)API.对于大多数应用来说,这样的API使用很方 便,然而,一些对 ...

  9. java多线程设计模式详解[推荐]

    java多线程设计模式详解[推荐] java多线程设计模式详解之一 线程的创建和启动 java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run ...

最新文章

  1. Nignx集成fastDFS后访问Nginx一直在加载中解决
  2. 车牌识别学习资料整理
  3. 又遇到问题:wrong ELF class: ELFCLASS32 in Unknown on line
  4. 能源项目xml文件 -- springMVC-servlet.xml
  5. Linux+Nginx+Asp.net Core部署
  6. C++primer 第 4 章 表达式 4.7条件运算符 4.8位运算符 4.9 sizeof运算符 4.10逗号运算符 4.11类型转换 4 . 1 2 运算符优先级表
  7. 移动云11.11,钜惠High不停!
  8. Linux块层技术全面剖析-v0.1
  9. MVC设计模式:概念,模型,视图,控制器
  10. 小新air14 2020 i5-1035G1完美黑苹果
  11. 关于我用过的机械键盘
  12. C++ 学生姓名学号 字符串
  13. 探寻平台经济健康发展和垄断规制
  14. 基于Python的时间序列异常值检测
  15. 数据结构题集(严书)查找 常见习题代码
  16. iOS启动速度优化总结
  17. DSP DSP汇编伪指令汇总
  18. 8、 高德离线地图开发教程
  19. [css] 如何使用Font Awesome
  20. JSTL(c标签)与Struts2(s标签)标签

热门文章

  1. Nat. Mach. Intell. | 深度神经网络中的捷径学习
  2. iScience | 大规模表征学习寻找分子间相互作用
  3. Drug Target Review | 虚拟现实(VR)用于新药设计
  4. SQLite简介与安装
  5. 第四课.LinuxShell编程
  6. 一个小团队使用的知识管理方案与工具
  7. Trends Genet | 王关红和Jackson Champer综述共生菌和基因驱动技术防控蚊媒疾病
  8. BiB:王秀杰/裴小兵合作开发单细胞组学细胞标记基因鉴定算法COSG
  9. 17日南土所蒋瑀霁报告:红壤团聚体尺度养分转化的生物学过程(线虫-微生物互作机制)...
  10. R语言使用psych包进行探索性因子分析EFA、使用cov2cor函数将原始数据的协方差矩阵将其转换为相关性矩阵( covariance matrix into correlation matrix)