散列表
Node节点(K-V)所放的位置:key的哈希值 & 数组.len-1
1.假如计算的下标为如图5,就会来到链表结构中,从头开始检查,看是否有key一致的节点,如果一致就进行替换,如果没有一致的节点就插入到末尾(要判断是否树化)
2.如果计算的下标为如图的10,可以看见是TreeBin节点(代表当前桶位已经树化成红黑树——可以提高查找的效率),其中TreeBin节点中维护了两个结构:红黑树结构和链表结构,先会插入到TreeNode链表结构,然后插入到TreeNode红黑树结构中(需要平衡操作)
3.如图的FWD节点 (扩容的时候可以用) ,处理完一个桶位之后,引用需要指向FWD节点,其他线程来到散列表时:1.如果是put操作,到FWD节点会帮你一起扩容。2.如果是get操作(FWD中保存了新表、扩容后表的引用)会到新表中执行查找操作。
4.扩容的顺序是从后往前迁移的——方便迭代的读,就是从前往后读的话,读到后面失去的数可以到新表去读(如果从低到高 容易冲突)
1_深入理解ConcurrentHashMap 常量
/* ---------------- Constants -------------- */
/**
* The largest possible table capacity. This value must be
* exactly 1<<30 to stay within Java array allocation and indexing
* bounds for power of two table sizes, and is further required
* because the top two bits of 32bit hash fields are used for
* control purposes.
* 散列表数组最大限制
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The default initial table capacity. Must be a power of 2
* (i.e., at least 1) and at most MAXIMUM_CAPACITY.
* 散列表默认值
*/
private static final int DEFAULT_CAPACITY = 16;
/**
* The largest possible (non-power of two) array size.
* Needed by toArray and related methods.
*/
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* The default concurrency level for this table. Unused but
* defined for compatibility with previous versions of this class.
* 并发级别,jdk1.7遗留下来的,1.8只有在初始化的时候用了一用。
* 不代表并发级别。
*/
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
/**
* The load factor for this table. Overrides of this value in
* constructors affect only the initial table capacity. The
* actual floating point value isn't normally used -- it is
* simpler to use expressions such as {@code n - (n >>> 2)} for
* the associated resizing threshold.
* 负载因子,JDK1.8中 ConcurrentHashMap 是固定值
*/
private static final float LOAD_FACTOR = 0.75f;
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2, and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
* 树化阈值,指定桶位 链表长度达到8的话,有可能发生树化操作。
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
* 红黑树转化为链表的阈值
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* The value should be at least 4 * TREEIFY_THRESHOLD to avoid
* conflicts between resizing and treeification thresholds.
* 联合TREEIFY_THRESHOLD控制桶位是否树化,只有当table数组长度达到64且 某个桶位 中的链表长度达到8,才会真正树化
*/
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* Minimum number of rebinnings per transfer step. Ranges are
* subdivided to allow multiple resizer threads. This value
* serves as a lower bound to avoid resizers encountering
* excessive memory contention. The value should be at least
* DEFAULT_CAPACITY.
* 线程迁移数据最小步长,控制线程迁移任务最小区间一个值
*/
private static final int MIN_TRANSFER_STRIDE = 16;
/**
* The number of bits used for generation stamp in sizeCtl.
* Must be at least 6 for 32bit arrays.
* 扩容相关,计算扩容时生成的一个标识戳(虽然没有用final修饰,但是全文没有修改他,不改变)
* 不管什么线程来 16扩容到32都是不变的
*/
private static int RESIZE_STAMP_BITS = 16;
/**
* The maximum number of threads that can help resize.
* Must fit in 32 - RESIZE_STAMP_BITS bits.
* 65535 表示并发扩容最多线程数
*/
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
/**
* The bit shift for recording size stamp in sizeCtl.
* 扩容相关
*/
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
/*
* Encodings for Node hash fields. See above for explanation.
*/
//node节点的hash为-1时,表示当前节点是FWD节点,已经迁移了
static final int MOVED = -1; // hash for forwarding nodes
//node节点的hash为-2时,表示当前节点已经树化,
// 表示当前节点为TreeBin节点,TreeBin节点代理操作红黑树
static final int TREEBIN = -2; // hash for roots of trees
// ReservationNode的hash值
static final int RESERVED = -3; // hash for transient reservations
//0x7fffffff =》0111 1111 1111 1111 1111 1111 1111 1111
//可以将一个负数 位与运算后得到正数,但是不是绝对值
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();
/** For serialization compatibility.
* JDK1.8序列化 为了兼容jdk1.7的ConcurrentHashMap保存的
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("segments", Segment[].class),
new ObjectStreamField("segmentMask", Integer.TYPE),
new ObjectStreamField("segmentShift", Integer.TYPE)
};
2_深入理解ConcurrentHashMap 成员变量
/* ---------------- Fields -------------- */
/**
* The array of bins. Lazily initialized upon first insertion.
* Size is always a power of two. Accessed directly by iterators.
* 散列表,长度一定是2次方数
*/
transient volatile Node<K,V>[] table;
/**
* The next table to use; non-null only while resizing.
* 扩容过程中,会将扩容中的新table 赋值给nextTable 保持引用,扩容结束之后,这里会被设置为Null
*/
private transient volatile Node<K,V>[] nextTable;
/**
* Base counter value, used mainly when there is no contention,
* but also as a fallback during table initialization
* races. Updated via CAS.
* 化整为零
* LongAdder 中的 baseCount 未发生竞争时 或者 当前LongAdder处于加锁状态时,增量累到到baseCount中
*/
private transient volatile long baseCount;
/**
* Table initialization and resizing control. When negative, the
* table is being initialized or resized: -1 for initialization,
* else -(1 + the number of active resizing threads). Otherwise,
* when table is null, holds the initial table size to use upon
* creation, or 0 for default. After initialization, holds the
* next element count value upon which to resize the table.
* sizeCtl < 0
* 1. -1 表示当前table正在初始化(有线程在创建table数组),当前线程需要自旋等待..
* 2.表示当前table数组正在进行扩容 ,高16位表示:扩容的标识戳 低16位表示:(1 + nThread) 当前参与并发扩容的线程数量
*
* sizeCtl = 0,表示创建table数组时 使用DEFAULT_CAPACITY为大小
*
* sizeCtl > 0
*
* 1. 如果table未初始化,表示初始化大小
* 2. 如果table已经初始化,表示下次扩容时的 触发条件(阈值)
*/
private transient volatile int sizeCtl;
/**
* The next table index (plus one) to split while resizing.
* 扩容过程中,记录当前进度。所有线程都需要从transferIndex中分配区间任务,去执行自己的任务。
*/
private transient volatile int transferIndex;
/**
* Spinlock (locked via CAS) used when resizing and/or creating CounterCells.
* LongAdder中的cellsBuzy 0表示当前LongAdder对象无锁状态,1表示当前LongAdder对象加锁状态)
* 只有一个对象能持有加锁状态
*
*/
private transient volatile int cellsBusy;
/**
* Table of counter cells. When non-null, size is a power of 2.
* LongAdder中的cells数组,当baseCount发生竞争后,会创建cells数组,
* 线程会通过计算hash值 取到 自己的cell ,将增量累加到指定cell中
* 总数 = sum(cells) + baseCount
*/
private transient volatile CounterCell[] counterCells;
// views
private transient KeySetView<K,V> keySet;
private transient ValuesView<K,V> values;
private transient EntrySetView<K,V> entrySet;
3_分解静态代码块
// Unsafe mechanics
private static final sun.misc.Unsafe U;
/**表示sizeCtl属性在ConcurrentHashMap中内存偏移地址*/
private static final long SIZECTL;
/**表示transferIndex属性在ConcurrentHashMap中内存偏移地址*/
private static final long TRANSFERINDEX;
/**表示baseCount属性在ConcurrentHashMap中内存偏移地址*/
private static final long BASECOUNT;
/**表示cellsBusy属性在ConcurrentHashMap中内存偏移地址*/
private static final long CELLSBUSY;
/**表示cellValue属性在CounterCell中内存偏移地址*/
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);
//表示数组单元所占用空间大小,scale 表示Node[]数组中每一个单元所占用空间大小
int scale = U.arrayIndexScale(ak);
//1 0000 & 0 1111 = 0
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
//numberOfLeadingZeros() 这个方法是返回当前数值转换为二进制后,从高位到低位开始统计,看有多少个0连续在一块。
//8 => 1000 numberOfLeadingZeros(8) = 28
//int是4个长度 100 32-3=29
//4 => 100 numberOfLeadingZeros(4) = 29
//ASHIFT = 31 - 29 = 2 ?? scale是局部变量
//下标为5的数据
//ABASE + (5 << ASHIFT)
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (Exception e) {
throw new Error(e);
}
}
4_分解内部类 Node、TreeNode、ForwardingNode
Node
static class Node<K,V> implements Map.Entry<K,V> {
//这个hash的K的hash经过一次扰动运算得出来的
final int hash;
final K key; //线程安全不能修改
volatile V val; //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.
* 链表情况下用不到,当前桶位变成 treebin fwd节点会用到
*/
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;
}
}
TreeNode
/**
* Nodes for use in TreeBins
*/
static final class TreeNode<K,V> extends Node<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next,
TreeNode<K,V> parent) {
super(hash, key, val, next);
this.parent = parent;
}
Node<K,V> find(int h, Object k) {
return findTreeNode(h, k, null);
}
/**
* Returns the TreeNode (or null if not found) for the given key
* starting at given root.
*/
final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
if (k != null) {
TreeNode<K,V> p = this;
do {
int ph, dir; K pk; TreeNode<K,V> q;
TreeNode<K,V> pl = p.left, pr = p.right;
if ((ph = p.hash) > h)
p = pl;
else if (ph < h)
p = pr;
else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
return p;
else if (pl == null)
p = pr;
else if (pr == null)
p = pl;
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
p = (dir < 0) ? pl : pr;
else if ((q = pr.findTreeNode(h, k, kc)) != null)
return q;
else
p = pl;
} while (p != null);
}
return null;
}
}
ForWaidingNode
/* ---------------- Special Nodes -------------- */
/**
* A node inserted at head of bins during transfer operations.
*/
static final class ForwardingNode<K,V> extends Node<K,V> {
//拿到fwd节点 代表现在再扩容数据正在迁移 写线程:需要参与并发扩容 读线程:调用find方法到新表继续查询
final Node<K,V>[] nextTable;
//hash固定
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
//tab 一定不为空
Node<K,V>[] tab = nextTable;
outer: for (;;) {
//n 表示为扩容而创建的 新表的长度
//e 表示在扩容而创建新表使用 寻址算法 得到的 桶位头结点
Node<K,V> e; int n;
//条件一:永远不成立
//条件二:永远不成立
//条件三:永远不成立
//条件四:在新扩容表中 重新定位 hash 对应的头结点
//true -> 1.在oldTable中 对应的桶位在迁移之前就是null
// 2.扩容完成后,有其它写线程,将此桶位设置为了null
if (k == null || tab == null || (n = tab.length) == 0 ||
(e = tabAt(tab, (n - 1) & h)) == null)
return null;
//前置条件:扩容后的表 对应hash的桶位一定不是null,e为此桶位的头结点
//e可能为哪些node类型?
//1.node 类型
//2.TreeBin 类型
//3.FWD 类型
for (;;) {
//eh 新扩容后表指定桶位的当前节点的hash
//ek 新扩容后表指定桶位的当前节点的key
int eh; K ek;
//条件成立:说明新扩容 后的表,当前命中桶位中的数据,即为 查询想要数据。
if ((eh = e.hash) == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
//eh<0
//1.TreeBin 类型 2.FWD类型(新扩容的表,在并发很大的情况下,可能在此方法 再次拿到FWD类型..)
if (eh < 0) {
if (e instanceof ForwardingNode) {
tab = ((ForwardingNode<K,V>)e).nextTable;
continue outer;
}
else
//说明此桶位 为 TreeBin 节点,使用TreeBin.find 查找红黑树中相应节点。
return e.find(h, k);
}
//前置条件:当前桶位头结点 并没有命中查询,说明此桶位是 链表
//1.将当前元素 指向链表的下一个元素
//2.判断当前元素的下一个位置 是否为空
// true->说明迭代到链表末尾,未找到对应的数据,返回Null
if ((e = e.next) == null)
return null;
}
}
}
}
5_小函数工具方法源码分解
5.1——spread(hash) 源码分析
/
* 1100 0011 1010 0101 0001 1100 0001 1110
* 0000 0000 0000 0000 1100 0011 1010 0101
* 1100 0011 1010 0101 1101 1111 1011 1011
* ---------------------------------------
* 1100 0011 1010 0101 1101 1111 1011 1011
* 0111 1111 1111 1111 1111 1111 1111 1111 ==HASH_BITS
* 0100 0011 1010 0101 1101 1111 1011 1011
*
* 当前的hash右移16位 亦或 原来的hash & HASH_BITS 得到正数的hash值
* 让高16位 也参与到运算中来
*/
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}
5.2——tabAt(tab, index) 源码分析
//获取数组指定下标位置的元素
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
5.3——casTabAt(tab, index, c, v )源码分析
//用cas的方式去table的指定位置设置值,设置成功返回true
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);
}
5.4——setTabAt(tab, index, v) 源码分析
//指定位置设置值
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
5.5——resizeStamp(int n) 源码分析
/**
* Returns the stamp bits for resizing a table of size n.
* Must be negative when shifted left by RESIZE_STAMP_SHIFT.
* 扩容标识戳 一致才能参与扩容
* 16 -> 32
* numberOfLeadingZeros(16) => 1 0000 =>27 =>0000 0000 0001 1011
* |
* (1 << (RESIZE_STAMP_BITS - 1)) => 1000 0000 0000 0000 => 32768
* ---------------------------------------------------------------
* 0000 0000 0001 1011
* 1000 0000 0000 0000
* 1000 0000 0001 1011
*/
static final int resizeStamp(int n) {
return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
}
5.6——tableSizeFor(int c) 源码分析
/**
* Returns a power of two table size for the given desired capacity.
* See Hackers Delight, sec 3.2
* 返回>=c的最小的2的次方数
* c=28
* n=27 => 0b 11011
* 11011 | 01101 => 11111
* 11111 | 00111 => 11111
* ....
* => 11111 + 1 =100000 = 32
*/
private static final int tableSizeFor(int c) {
int n = c - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}