2019独角兽企业重金招聘Python工程师标准>>>

ConcurrentHashMap是java中为了解决HashMap不能支持高并发而设计的新的实现。

ConcurrentHashMap的类结构

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

ConcurrentHashMap的主要成员变量

//容量最大值
private static final int MAXIMUM_CAPACITY = 1 << 30;
//默认容量大小
private static final int DEFAULT_CAPACITY = 16;
//数组容量的最大值
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//默认的并发数
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
//负载因子
private static final float LOAD_FACTOR = 0.75f;
//由链表转为红黑树的阈值
static final int TREEIFY_THRESHOLD = 8;
//由红黑树转为链表的阈值
static final int UNTREEIFY_THRESHOLD = 6;
//转换为红黑树的最小容量
static final int MIN_TREEIFY_CAPACITY = 64;
//每次进行转移的最小值
private static final int MIN_TRANSFER_STRIDE = 16;
//生成sizeCtl所使用的最小bit位数
private static int RESIZE_STAMP_BITS = 16;
//进行扩容锁需要的最大线程数
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
//记录sizeCtl的大小所需要进行的偏移位数
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
//标识
static final int MOVED     = -1; // hash for forwarding nodes
static final int TREEBIN   = -2; // hash for roots of trees
static final int RESERVED  = -3; // hash for transient reservations
static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
/** Number of CPUS, to place bounds on some sizings */
//cpu的个数
static final int NCPU = Runtime.getRuntime().availableProcessors();
//存储元素的数组
transient volatile Node<K,V>[] table;
//扩容时新生成的数组,用于下一个存放元素的数组,其大小为原数组的两倍
private transient volatile Node<K,V>[] nextTable;
//基本计数
private transient volatile long baseCount;
/**
* hash表初始化或扩容时的一个控制位标识量。
* 负数代表正在进行初始化或扩容操作
* -1代表正在初始化
* -N 表示有N-1个线程正在进行扩容操作
* 正数或0代表hash表还没有被初始化,这个数值表示初始化或下一次进行扩容的大小
*/
private transient volatile int sizeCtl;
//扩容下另一个表的索引
private transient volatile int transferIndex;
//
private transient volatile int cellsBusy;
//
private transient volatile CounterCell[] counterCells;//以下是通过sun.misc.Unsafe的objectFieldOffset方法获取成员变量在class域中的偏移值
private static final sun.misc.Unsafe U;
private static final long SIZECTL;
private static final long TRANSFERINDEX;
private static final long BASECOUNT;
private static final long CELLSBUSY;
private static final long CELLVALUE;
private static final long ABASE;
private static final int ASHIFT;
static {try {U = sun.misc.Unsafe.getUnsafe();Class<?> k = ConcurrentHashMap.class;SIZECTL = U.objectFieldOffset(k.getDeclaredField("sizeCtl"));TRANSFERINDEX = U.objectFieldOffset(k.getDeclaredField("transferIndex"));BASECOUNT = U.objectFieldOffset(k.getDeclaredField("baseCount"));CELLSBUSY = U.objectFieldOffset(k.getDeclaredField("cellsBusy"));Class<?> ck = CounterCell.class;CELLVALUE = U.objectFieldOffset(ck.getDeclaredField("value"));Class<?> ak = Node[].class;ABASE = U.arrayBaseOffset(ak);int scale = U.arrayIndexScale(ak);if ((scale & (scale - 1)) != 0)throw new Error("data type scale not a power of two");ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);} catch (Exception e) {throw new Error(e);}}

ConcurrentHashMap中的主要内部类

    Node:

static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;volatile V val;volatile Node<K,V> next;Node(int hash, K key, V val, Node<K,V> next) {this.hash = hash;this.key = key;this.val = val;this.next = next;}public final K getKey()       { return key; }public final V getValue()     { return val; }public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }public final String toString(){ return key + "=" + val; }public final V setValue(V value) {throw new UnsupportedOperationException();}public final boolean equals(Object o) {Object k, v, u; Map.Entry<?,?> e;return ((o instanceof Map.Entry) &&(k = (e = (Map.Entry<?,?>)o).getKey()) != null &&(v = e.getValue()) != null &&(k == key || k.equals(key)) &&(v == (u = val) || v.equals(u)));}/*** Virtualized support for map.get(); overridden in subclasses.*/Node<K,V> find(int h, Object k) {Node<K,V> e = this;if (k != null) {do {K ek;if (e.hash == h &&((ek = e.key) == k || (ek != null && k.equals(ek))))return e;} while ((e = e.next) != null);}return null;}
}

由于这里Node和HashMap中的Node基本一致,所以不再赘述。

    ForwardingNode:继承Node节点,hash值为-1,其中存储nextTable的引用。

static final class ForwardingNode<K,V> extends Node<K,V> {final Node<K,V>[] nextTable;ForwardingNode(Node<K,V>[] tab) {super(MOVED, null, null, null);this.nextTable = tab;}Node<K,V> find(int h, Object k) {// loop to avoid arbitrarily deep recursion on forwarding nodesouter: for (Node<K,V>[] tab = nextTable;;) {Node<K,V> e; int n;if (k == null || tab == null || (n = tab.length) == 0 ||(e = tabAt(tab, (n - 1) & h)) == null)return null;for (;;) {int eh; K ek;if ((eh = e.hash) == h &&((ek = e.key) == k || (ek != null && k.equals(ek))))return e;if (eh < 0) {if (e instanceof ForwardingNode) {tab = ((ForwardingNode<K,V>)e).nextTable;continue outer;}elsereturn e.find(h, k);}if ((e = e.next) == null)return null;}}}}

ReservationNode:继承于Node,哈希值为-3。

static final class ReservationNode<K,V> extends Node<K,V> {ReservationNode() {super(RESERVED, null, null, null);}Node<K,V> find(int h, Object k) {return null;}
}

    ConcurrentHashMap的主要构造函数

//设置容量值的构造函数
public ConcurrentHashMap(int initialCapacity) {if (initialCapacity < 0)throw new IllegalArgumentException();//根据容量值计算sizeCtl//MAXIMUM_CAPACITY >>> 1 允许的最大容量值无符号右移一位,(1 << 30)>>>1//如果参数容量值大于等于参数容量值,sizeCtl直接为允许的最大容量值//否则,initialCapacity + (initialCapacity >>> 1) + 1,大概为initialCapacity 的1.5倍//tableSizeFor方法咱们再HashMap中分析过了,是取大于参数的最小二次幂,比如参数为15,结果就为16int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?MAXIMUM_CAPACITY :tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));//设置sizeCtl值this.sizeCtl = cap;
}//构造参数为Map的构造方法
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {//sizeCtl直接为默认容量值this.sizeCtl = DEFAULT_CAPACITY;putAll(m);
}//设置容量值、负载因子的构造函数
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 bins//如果容量值小于并发等级,则将容量值设置成并发等级,也就是容量值不能小于并发等级initialCapacity = concurrencyLevel;   // as estimated threads//容量值除以负载因子,可以将容量值看成阈值,然后反推容量值,这里size就是反推的容量值long size = (long)(1.0 + (long)initialCapacity / loadFactor);//根据容量值计算sizeCtl,该方法上面已经分析过了int cap = (size >= (long)MAXIMUM_CAPACITY) ?MAXIMUM_CAPACITY : tableSizeFor((int)size);this.sizeCtl = cap;
}

我们发现构造函数中都只是对sizeCtl进行了初始化,其余成员变量,比如table数组,均没有初始化,而是等到第一次put操作时进行初始化。

    ConcurrentHashMap的主要方法

    我们先来看看ConcurrentHashMap的一些基础方法。

散列计算:int spread(int h):对key的hashCode值进行散列计算。

//对key值的hashCode值进行散列
static final int spread(int h) {return (h ^ (h >>> 16)) & HASH_BITS;
}

原子操作方法:tabAt、casTabAt、setTabAt。这是三个原子操作,用于对指定位置的节点进行操作。正是这些原子操作保证了ConcurrentHashMap的线程安全。

//获得数组table中在i位置上的Node节点
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {//这里U就是UnSafe类的实例return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}//利用CAS算法设置i位置上的Node节点。之所以能实现并发是因为他指定了原来这个节点的值是多少
//在CAS算法中,会比较内存中的值与你指定的这个值是否相等,如果相等才接受你的修改,否则拒绝你的修改
//因此当前线程中的值并不是最新的值,这种修改可能会覆盖掉其他线程的修改结果,ABA问题
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,Node<K,V> c, Node<K,V> v) {return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}//利用volatile方法设置数组table中位置为i的node
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}

初始化数组:Node<K,V>[] initTable()

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.compareAndSwapInt(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;
}

获取:V get(Object key)

public V get(Object key) {//定义两个类型为Node数组的局部变量tab和e//定义两个int类型的局部变量n、eh//定义类型为泛型的局部变量ekNode<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;//对key的hashCode进行散列计算int h = spread(key.hashCode());//将当前node数组table赋值给tab,将node数组长度赋值给nif ((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;}

转载于:https://my.oschina.net/u/3765527/blog/1832989

并发编程---ConcurrentHashMap源码解析相关推荐

  1. 并发编程与源码解析 (三)

    并发编程 (三) 1 Fork/Join分解合并框架 1.1 什么是fork/join ​ Fork/Join框架是JDK1.7提供的一个用于并行执行任务的框架,开发者可以在不去了解如Thread.R ...

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

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

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

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

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

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

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

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

  6. ConcurrentHashMap源码解析(1)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 注:在看这篇文章之前,如果对HashMap的层不清楚的话,建议先去看看HashMap源码解析. http:/ ...

  7. hashmap与concurrenthashmap源码解析

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

  8. JDK8中ConcurrentHashMap源码解析

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

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

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

最新文章

  1. Java 多线程同步和异步详解
  2. mysql触发器的简单写法
  3. 【记录】jenkins 安装及环境配置(二)
  4. 从技术平台到aPaaS平台
  5. react-native 框架升级 安卓第三方插件报错 Android resource linking failed
  6. cgi web页面传入命令
  7. DBA_实践指南系列7_Oracle Erp R12监控OAM(案例)
  8. 黄海造船周身流动的信息化血液
  9. 还不会回答Spring Boot和Spring MVC的关系?大厂Java高级面试官告诉你答案!
  10. 厦门高考成绩查询2021,2021厦门市地区高考成绩排名查询,厦门市高考各高中成绩喜报榜单...
  11. 企业如何操作网络口碑营销?
  12. 【日常计算机问题】win11、win10解决公共WiFi认证不弹出的问题。电脑没有弹出认证界面。以广州图书馆i-guangdong;i广东为例
  13. 阿木P230无人机指点飞行实验记录
  14. kcl计算机通信专业,KCL伦敦国王学院计算机和电子工程介绍
  15. 打怪升级记录(2018-2019)
  16. 小米笔记本电脑怎么使用U盘重装系统教学
  17. mysql建图书馆表_创建学校图书馆数据库 BookDB
  18. ubuntu返回图形界面_Ubuntu设置命令行界面和图形界面切换方法
  19. P4287 [SHOI2011]双倍回文
  20. 《UNIX环境高级编程中文版》pdf

热门文章

  1. corosync+openais+pacemaker构建高可用性集群
  2. 用异或的性质实现简单加密解密
  3. Practice Lab 7:路由再分发
  4. NFS网络文件系统服务配置、验证及错误解决
  5. 神级代码编辑软件(Sublime Text 3) v3.3114 汉化特别版
  6. HDU2012 素数判定
  7. win7下使用命令行关闭被某一端口占用的进程
  8. office2007右键doc,xls
  9. 利用 S3-tests 测试 S3 接口兼容性
  10. Cable--新虚拟网络架构介绍