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

posted @ 2018-03-09 17:00  wzbin  阅读(309)  评论(0编辑  收藏  举报