ConcurrentHashMap是java中为了解决HashMap不能支持高并发而设计的新的实现。java
ConcurrentHashMap的类结构node
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 nodes outer: 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; } else return 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的主要构造函数oop
//设置容量值的构造函数 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,结果就为16 int 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操做时进行初始化。this
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 spin else 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 //定义类型为泛型的局部变量ek Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek; //对key的hashCode进行散列计算 int h = spread(key.hashCode()); //将当前node数组table赋值给tab,将node数组长度赋值给n 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; }