Java1.8-ConcurrentHashMap
J.U.C体系结构 -java并发框架 包:package java.util.concurrent;
CAS:compare and swap : CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。
如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。
1 static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
2 return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE); //偏移量+ABASE(table中首个元素的偏移地址)
3 }
1 static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v) { 2 return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v); 3 }
1 static final int MOVED = -1; // hash for forwarding nodes 2 static final int TREEBIN = -2; // hash for roots of trees 3 static final int RESERVED = -3; // hash for transient reservations
sizeCtl
if table未初始化: =0 //未指定初始容量时的默认值 >0 //指定初始容量(非传入值,是2的幂次修正值)大小的两倍 =-1 //表明table正在初始化 else if nextTable为空: if 扩容时发生错误(如内存不足、table.length * 2 > Integer.MAX_VALUE等): =Integer.MAX_VALUE //不必再扩容了! else: =table.length * 0.75 //扩容阈值调为table容量大小的0.75倍 else: =-(1+N) //N的低RESIZE_STAMP_SHIFT位表示参与扩容线程数,后面详细介绍 ?????
构造:
1 public ConcurrentHashMap(int initialCapacity) { 2 if (initialCapacity < 0) 3 throw new IllegalArgumentException(); 4 int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? 5 MAXIMUM_CAPACITY : 6 tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); 7 this.sizeCtl = cap; 8 }
初始化表:
1 private final Node<K,V>[] initTable() { 2 Node<K,V>[] tab; int sc; 3 while ((tab = table) == null || tab.length == 0) { 4 if ((sc = sizeCtl) < 0) //sizeCtl状态字段 5 Thread.yield(); // lost initialization race; just spin 不发生初始化进程的竞争 6 else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {//重置sizeCtl为-1,表示正在初始化 7 try { 8 if ((tab = table) == null || tab.length == 0) { 9 int n = (sc > 0) ? sc : DEFAULT_CAPACITY;//DEFAULT_CAPACITY=16 10 @SuppressWarnings("unchecked") 11 Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; 12 table = tab = nt; 13 sc = n - (n >>> 2);//75% 14 } 15 } finally { 16 sizeCtl = sc;//全局变量值为sc,但在SIZECTL位置上是的sizeCtl为-1 17 } 18 break; 19 } 20 } 21 return tab; 22 }
tableSizeFor
1 private static final int tableSizeFor(int c) {
//全部变为1 最后+1——比当前数大的最小的2的次方数 2 int n = c - 1;//initialCapacity+initialCapacity/2 3 n |= n >>> 1; 4 n |= n >>> 2; 5 n |= n >>> 4; 6 n |= n >>> 8; 7 n |= n >>> 16; 8 return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; 9 }
先看put:
1 final V putVal(K key, V value, boolean onlyIfAbsent) {
2 if (key == null || value == null) throw new NullPointerException();
3 int hash = spread(key.hashCode());//(h ^ (h >>> 16)) & 0x7fffffff
//最简单情况:0<h<2^16,so h >>> 16:0...0,so (h^h>>>16)=h,so (h ^ (h >>> 16)) & 0x7fffffff=h&(01...1)=h
//h>2^16或h<-2^16取高位运算;h<0保留符号位;
4 int binCount = 0;
5 for (Node<K,V>[] tab = table;;) {
6 Node<K,V> f; int n, i, fh;
7 if (tab == null || (n = tab.length) == 0)
8 tab = initTable();
9 else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//取链表头
//i=(n-1)&hash 等价于i=hash%n(前提是n为2的幂次方)
//链表头的内存地址与i有关:针对当前table的大小n,hash算法得出特有的i
//((long)i << ASHIFT) + ABASE tabAt为方法中偏移后内存地址
10 if (casTabAt(tab, i, null,
11 new Node<K,V>(hash, key, value, null)))//CAS新增
12 break; // no lock when adding to empty bin
13 }
14 else if ((fh = f.hash) == MOVED)//MOVED=-1 扩容时会打上标识 即ForwardingNode 下面会有 标识当前node已经迁移了
15 tab = helpTransfer(tab, f);
16 else {
17 V oldVal = null;
18 synchronized (f) {
19 if (tabAt(tab, i) == f) {//内存位置没有改变
20 if (fh >= 0) { //fh〉0 说明这个节点是一个链表的节点 不是树的节点
21 binCount = 1;
22 for (Node<K,V> e = f;; ++binCount) {
23 K ek;
24 if (e.hash == hash &&
25 ((ek = e.key) == key ||
26 (ek != null && key.equals(ek)))) {//同hash同key
27 oldVal = e.val;
28 if (!onlyIfAbsent)//default:!false
29 e.val = value; //覆盖值
30 break;
31 }
32 Node<K,V> pred = e;
33 if ((e = e.next) == null) {//取到链尾,新增
34 pred.next = new Node<K,V>(hash, key, value, null);
36 break;
37 }
38 }
39 }
40 else if (f instanceof TreeBin) {//树操作
41 Node<K,V> p;
42 binCount = 2;
43 if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
44 value)) != null) {
45 oldVal = p.val;
46 if (!onlyIfAbsent)
47 p.val = value;
48 }
49 }
50 }
51 }
52 if (binCount != 0) {
53 if (binCount >= TREEIFY_THRESHOLD)//链转树
54 treeifyBin(tab, i);
55 if (oldVal != null)
56 return oldVal;
57 break;
58 }
59 }
60 }
61 addCount(1L, binCount);//
62 return null;
63 }
private transient volatile CounterCell[] counterCells;
counterCell
1 @sun.misc.Contended static final class CounterCell {
2 volatile long value;
3 CounterCell(long x) { value = x; }
4 }
sumCount
final long sumCount() { CounterCell[] as = counterCells; CounterCell a; long sum = baseCount; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) sum += a.value; } } return sum; }
addCount
private final void addCount(long x, int check) {
CounterCell[] as; long b, s;
if ((as = counterCells) != null ||
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {//s = baseCount+x baseCount初始为0,x默认为1
//counterCells==null 或者 baseCount被其他线程改变
//counterCells的赋值在fullAddCount中
CounterCell a; long v; int m;
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
//多线程CAS发生失败的时候执行
fullAddCount(x, uncontended);
return;
}
if (check <= 1)
return;
s = sumCount();
}
if (check >= 0) {//check链上的索引 链头时为0
Node<K,V>[] tab, nt; int n, sc;
while (s >= (long)(sc = sizeCtl) //达到扩容阈值
&& (tab = table) != null //table已初始化
&&(n = tab.length) < MAXIMUM_CAPACITY) {//小于最大扩容值
int rs = resizeStamp(n);
if (sc < 0) {
//sc>>>16:右移16位,高位补0(放弃高位),原高位变地位;
//检查是原容量为n的情况下进行扩容,保证sizeCtl与n是一块修改好的
//条件2与条件3在当前RESIZE_STAMP_BITS情况下应该不会成功,欢迎指正。
//条件4与条件5确保tranfer()中的nextTable相关初始化逻辑已走完。
if ((sc >>> RESIZE_STAMP_SHIFT) != rs ||
sc == rs + 1 || sc == rs + MAX_RESIZERS ||
(nt = nextTable) == null || transferIndex <= 0)
break;
//此时sc为负数,sc+1意味减少一个线程?
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
//当前线程第一个或唯一的在扩容的线程
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
//给SIZECTL赋值为rs_2,此后每多一个线程transfer,sc + 1
//rs左移16位为负,低位补0。
//当n=4时,rs = 29 | 0...0(16个)10...0(15个) = 0...011101 | 0...0(16个)10...0(15个) = 0...0(16个)10..0(10个)11101
//rs << RESIZE_STAMP_SHIFT + 2 = 10..0(10个)11101...0(17个) + 2 = rs_2 最后+2即低位补0...010resizeStamp值的意义:高16位- 用做扩容标记 ;低16位- 用做并行扩容线程数(符号位为1)如上:resizeStamp值的意义:高16位- 用做扩容标记 ;低16位- 用做并行扩容线程数(符号位为1)))
transfer(tab, null);
s = sumCount();
}
}
}
resizeStamp值的意义:高16位- 用做扩容标记与扩容时n相关 ;低16位- 用做并行扩容线程数(此时符号位为1)
resizeStamp
/**
* The number of bits used for generation stamp in sizeCtl.
* Must be at least 6 for 32bit arrays.
*/
private static int RESIZE_STAMP_BITS = 16;
/**
* The bit shift for recording size stamp in sizeCtl.
*/
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS = 16;
1 static final int resizeStamp(int n) {
2 return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
//n的2进制前面有多少个0(cn) | 0...0(16个)10...0(15个) (RESIZE_STAMP_BITS=16)
//n是2的幂次,所以返回值rs对于不同容量大小的table值必然不同
//cn最大为32即100000,即cn的前(32-6)位数必然全是0,即resizeStamp的前16位必然全是0,第17位为1,18位到26位都是0
//
3 }
ForwardingNode :扩容时用的Node,ForwardingNode.hash=-1,nextTable(ForwardingNode内部属性)由构造传入。
1 static final class ForwardingNode<K,V> extends Node<K,V> {
2 final Node<K,V>[] nextTable;
3 ForwardingNode(Node<K,V>[] tab) {
4 super(MOVED=-1, null, null, null);
5 this.nextTable = tab;
6 }
7
8 Node<K,V> find(int h, Object k) {
9 // loop to avoid arbitrarily deep recursion on forwarding nodes
10 outer: for (Node<K,V>[] tab = nextTable;;) {
11 Node<K,V> e; int n;
12 if (k == null || tab == null || (n = tab.length) == 0 ||
13 (e = tabAt(tab, (n - 1) & h)) == null)
14 return null;
15 for (;;) {
16 int eh; K ek;
17 if ((eh = e.hash) == h &&
18 ((ek = e.key) == k || (ek != null && k.equals(ek))))
19 return e;
20 if (eh < 0) {
21 if (e instanceof ForwardingNode) {
22 tab = ((ForwardingNode<K,V>)e).nextTable;
23 continue outer;
24 }
25 else
26 return e.find(h, k);
27 }
28 if ((e = e.next) == null)
29 return null;
30 }
31 }
32 }
33 }
private transient volatile Node<K,V>[] nextTable;(ConcurrentHashMap类属性)
注意
1.ConcurrentHashMap中只有在transfer方法中对ForwardingNode有过实例化
2.在new ForwardingNode前,transfer方法对 nextTable(ConcurrentHashMap类属性)、transferIndex赋值。nextTab也是ForwardingNode构造的入参。
——所以保证了在helpTransfer下nextTable不会为空,transferIndex也应有值
if (nextTab == null) { // initiating
try {
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
nextTab = nt;
} catch (Throwable ex) { // try to cope with OOME
sizeCtl = Integer.MAX_VALUE;
return;
}
nextTable = nextTab;
transferIndex = n;
}
...
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
...
setTabAt(tab, i, fwd);
具体看下transfer:
1 private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
2 int n = tab.length, stride;
3 if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE) //NCPU CPU核数 默认每核8线程
4 stride = MIN_TRANSFER_STRIDE; // subdivide range 每个线程处理桶的最小数目为16 桶——链
5 if (nextTab == null) { // initiating
6 try {
7 @SuppressWarnings("unchecked")
8 Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];//扩容*2
9 nextTab = nt;
10 } catch (Throwable ex) { // try to cope with OOME
11 sizeCtl = Integer.MAX_VALUE; //new Node<?,?>[n << 1]左移致负 异常
12 return;
13 }
14 nextTable = nextTab;
15 transferIndex = n;//tab.length
16 }
17 int nextn = nextTab.length;
18 ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
19 boolean advance = true;
20 boolean finishing = false; // to ensure sweep before committing nextTab
21 for (int i = 0, bound = 0;;) {
22 Node<K,V> f; int fh;
23 while (advance) {
24 int nextIndex, nextBound;
//顺序是3-1-1-...-1-2
//3(初始化bound、起始i);1(i递减);2(--i<bound,transferIndex=0)
25 if (--i >= bound || finishing)//i递减
26 advance = false;//下面(f = tabAt(tab, i)) == null判断返回后,i=n-1,bound=0;进入advance=false,再循环
//分配完,或者还未开始分配(默认值):nextIndex = transferIndex=0
27 else if ((nextIndex = transferIndex) <= 0) {//赋值nextIndex = transferIndex = tab.length;
28 i = -1;
29 advance = false;
30 }
31 else if (U.compareAndSwapInt
32 (this, TRANSFERINDEX, nextIndex,
33 nextBound = (nextIndex > stride ?
34 nextIndex - stride : 0))) {
//transferIndex(操作当前索引)减少已分配出去的桶,每次最多迁移stride个。如果返回false说明tab.length改变过,即有另一个线程在做扩容。再次循环
//当transferindex没有其他线程参与改变时,进入迁移操作。整个类中transferIndex仅在此有过赋值!
//或者当扩容完成,transferIndex为0,进去上一个判断,跳出循环。
35 bound = nextBound;//必>=0
36 i = nextIndex - 1;//transferIndex-1;
//i = nextIndex - 1 > nextIndex -stride =bound;从这走后就可进入第一个判断;且transferIndex>=0
37 advance = false;//暂时离开循环
38 }
39 }
//(nextIndex = transferIndex) <= 0;
40 if (i < 0 || i >= n || i + n >= nextn) { //扩容完成 nextn = nextTab.length;i+n >= n+n = nextn
41 int sc;
42 if (finishing) {//扩容完成准确标识
43 nextTable = null;
44 table = nextTab;
45 sizeCtl = (n << 1) - (n >>> 1);//*1.5
46 return;
47 }
//利用CAS方法更新这个扩容阈值,在这里面sizectl值减一,说明一个线程结束扩容
//此时sizectl = resizeStamp(n) << RESIZE_STAMP_SHIFT +2(addCount )
48 if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
49 if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)//即sizectl的值改变了,比如(n << 1) - (n >>> 1) 活干完了
50 return;
51 finishing = advance = true;
52 i = n; // recheck before commit n-\table的length
53 }
54 }
//实际存入节点时用的是(n-1)&hash 即0<hash%n<n 而i=n-1即倒序查找第一个非空链,放入ForwardingNode
55 else if ((f = tabAt(tab, i)) == null)
56 advance = casTabAt(tab, i, null, fwd);
//已处理的ForwardingNode
57 else if ((fh = f.hash) == MOVED)//当前node已经被迁移了 换成了ForwardingNode
58 advance = true; // already processed
59 else {
60 synchronized (f) {
61 if (tabAt(tab, i) == f) {//确报锁住f后,再次判断f位置
62 Node<K,V> ln, hn;
63 if (fh >= 0) {
//以下的部分在完成的工作是构造两个链表 一个是原链表 另一个是原链表的反序排列
64 int runBit = fh & n;//判断原数组中的节点的hash的 log(n)位为0或者1
65 Node<K,V> lastRun = f;
//找出该条链中,最后段p.hash&n不变的链段——即p.hash不变
66 for (Node<K,V> p = f.next; p != null; p = p.next) {
67 int b = p.hash & n;//p.hash & n不变
68 if (b != runBit) {
69 runBit = b;
70 lastRun = p;
71 }
72 }
73 if (runBit == 0) {
74 ln = lastRun;//指向链表的最后出现连续log(n)位为0的第一个节点
75 hn = null;
76 }
77 else {
78 hn = lastRun;//指向链表的最后出现连续log(n)位为1的第一个节点 ?
79 ln = null;
80 }
81 for (Node<K,V> p = f; p != lastRun; p = p.next) {
82 int ph = p.hash; K pk = p.key; V pv = p.val;
83 if ((ph & n) == 0)
84 ln = new Node<K,V>(ph, pk, pv, ln);
85 else
86 hn = new Node<K,V>(ph, pk, pv, hn);
87 }
//1.6版本扩容套路,假设最后一段完整的fh&n不变的链表的runbit都是0,
//则nextTab[i]内元素ln前逆序,ln及其之后顺序
//nextTab[i+n]内元素全部相对原table逆序。
88 setTabAt(nextTab, i, ln);
89 setTabAt(nextTab, i + n, hn);
//插入ForwardingNode表示已迁移
90 setTabAt(tab, i, fwd);
91 advance = true;
92 }
93 else if (f instanceof TreeBin) {
94 TreeBin<K,V> t = (TreeBin<K,V>)f;
95 TreeNode<K,V> lo = null, loTail = null;
96 TreeNode<K,V> hi = null, hiTail = null;
97 int lc = 0, hc = 0;
98 for (Node<K,V> e = t.first; e != null; e = e.next) {
99 int h = e.hash;
100 TreeNode<K,V> p = new TreeNode<K,V>
101 (h, e.key, e.val, null, null);
102 if ((h & n) == 0) {
103 if ((p.prev = loTail) == null)
104 lo = p;
105 else
106 loTail.next = p;
107 loTail = p;
108 ++lc;
109 }
110 else {
111 if ((p.prev = hiTail) == null)
112 hi = p;
113 else
114 hiTail.next = p;
115 hiTail = p;
116 ++hc;
117 }
118 }
119 ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
120 (hc != 0) ? new TreeBin<K,V>(lo) : t;
121 hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
122 (lc != 0) ? new TreeBin<K,V>(hi) : t;
123 setTabAt(nextTab, i, ln);
124 setTabAt(nextTab, i + n, hn);
125 setTabAt(tab, i, fwd);
126 advance = true;
127 }
128 }
129 }
130 }
131 }
132 }
helpTransfer
每个线程想增/删元素时,如果访问的桶是ForwardingNode节点,则表明当前正处于扩容状态,协助一起扩容完成后再完成相应的数据更改操作。
1 final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) { 2 Node<K,V>[] nextTab; int sc; 3 if (tab != null && (f instanceof ForwardingNode) && 4 (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) { 5 int rs = resizeStamp(tab.length);
6 while (nextTab == nextTable && table == tab &&
7 (sc = sizeCtl) < 0) { //nextTable在transfer定义 sc为负说明在扩容
//检查是原容量为n的情况下进行扩容,保证sizeCtl与n是一块修改好的。
//sc>>>16:右移16位,高位补0(放弃高位),原高位变地位;rs为当前线程数(为负) 8 if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || 9 sc == rs + MAX_RESIZERS || transferIndex <= 0) 10 break; 11 if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {//有新线程参与扩容则sizeCtl加1 12 transfer(tab, nextTab);//调用多个工作线程(transfer)一起帮助进行扩容 13 break; 14 } 15 } 16 return nextTab; 17 } 18 return table; 19 }
resizeStamp
1 public V get(Object key) { 2 Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek; 3 int h = spread(key.hashCode()); //return (h ^ (h >>> 16)) & HASH_BITS=0x7fffffff; 4 if ((tab = table) != null && (n = tab.length) > 0 && //1.8的ConcurrentHashMap中segment只为了兼容1.7版本,实际结构和1.8的HashMap类似 5 (e = tabAt(tab, (n - 1) & h)) != null) {
//tabAt:return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
//U:private static final sun.misc.Unsafe
//J.U.C体系结构(java.util.concurrent) java并发框架 6 if ((eh = e.hash) == h) { 7 if ((ek = e.key) == key || (ek != null && key.equals(ek)))//hash和key都正确 8 return e.val; 9 } 10 else if (eh < 0)//hash为负值表示正在扩容,这个时候查的是ForwardingNode的find方法来定位到nextTable来 11 return (p = e.find(h, key)) != null ? p.val : null; 12 while ((e = e.next) != null) {//hash正确但key不正确 13 if (e.hash == h && 14 ((ek = e.key) == key || (ek != null && key.equals(ek)))) 15 return e.val; 16 } 17 } 18 return null; 19 }
深入并发包 ConcurrentHashMap-http://www.importnew.com/26049.html
ConcurrentHashMap总结-http://www.importnew.com/22007.html
《Java源码分析》:ConcurrentHashMap JDK1.8-http://blog.csdn.net/u010412719/article/details/52145145
更好地理解jdk1.8中ConcurrentHashMap实现机制-http://blog.csdn.net/tp7309/article/details/76532366
探索jdk8之ConcurrentHashMap 的实现机制-http://www.cnblogs.com/huaizuo/archive/2016/04/20/5413069.html