相较于java7中的ConcurrentHashMap,java8的实现有了较大的改变。不同于java7中采纳segment+HashEntry数组+链表构造,其采纳了Node+链表+红黑树构造。并应用了Synchronized+CAS来管制并发
put办法
public V put(K key, V value) { return putVal(key, value, false);} final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException(); int hash = spread(key.hashCode()); // 记录链表长度 int binCount = 0; // 进行自旋 for (Node<K,V>[] tab = table; ; ) { // 留神这些变量所代表的含意 Node<K,V> f; int n, i, fh; K fk; V fv; // 数组为空的状况下,进行初始化 if (tab == null || (n = tab.length) == 0) // 初始化数组 tab = initTable(); // 找到hash值对应下标的的第一个节点 else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // 应用CAS,尝试将node放入。如果数组该地位是空的,那么第一次就会胜利。放入后break即可 // 如果CAS失败,则阐明有并发。持续循环进行下次操作 if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value))) break; // no lock when adding to empty bin } // 这个节点正处于扩容状态 else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); // 如果onlyIfAbsent为true,判断以后存在的节点和put的值是否雷同并返回 else if (onlyIfAbsent // check first node without acquiring lock && fh == hash && ((fk = f.key) == key || (fk != null && key.equals(fk))) && (fv = f.val) != null) return fv; else { //此时f为头节点并且不为空 V oldVal = null; // 获取监视器锁 synchronized (f) { if (tabAt(tab, i) == f) { // fh>=0,阐明是链表 if (fh >= 0) { // binCount用来记录链表长度 binCount = 1; // 遍历链表 for (Node<K,V> e = f;; ++binCount) { K ek; // 如果找到了对应的key,通过onlyIfAbsent判断是否进行valuie笼罩 // 之后就可break了 if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { oldVal = e.val; if (!onlyIfAbsent) e.val = value; break; } Node<K,V> pred = e; // 没找到对应的key,就将新的node置于链表尾部 if ((e = e.next) == null) { pred.next = new Node<K,V>(hash, key, value); break; } } } // 红黑树 else if (f instanceof TreeBin) { Node<K,V> p; binCount = 2; // 应用红黑树的putTreeVal办法 if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) { oldVal = p.val; if (!onlyIfAbsent) p.val = value; } } // ReservationNode是外部办法应用的节点,可疏忽 else if (f instanceof ReservationNode) throw new IllegalStateException("Recursive update"); } } if (binCount != 0) { // 判断是否将链表转换为红黑树 if (binCount >= TREEIFY_THRESHOLD) // 此办法不肯定会进行红黑树的转换 // 如果数组长度大于64,会抉择进行数组扩容。 treeifyBin(tab, i); if (oldVal != null) return oldVal; break; } } } addCount(1L, binCount); return null;}
初始化数组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 // CAS将sizeCtl设为-1,示意抢到了锁 else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) { try { if ((tab = table) == null || tab.length == 0) { // 默认初始容量为16 int n = (sc > 0) ? sc : DEFAULT_CAPACITY; @SuppressWarnings("unchecked") // 初始化数组 Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; table = tab = nt; // 如果n为16,则sc = 0.75 * n sc = n - (n >>> 2); } } finally { // 设置sizeCtl sizeCtl = sc; } break; } } return tab;}
链表转红黑树treeifyBin
private final void treeifyBin(Node<K,V>[] tab, int index) { Node<K,V> b; int n; if (tab != null) { // 如果数组长度小于64。会进行数组扩容 // 因为数组的长度都是2的幂数,所以可能会是32、16、8...等 if ((n = tab.length) < MIN_TREEIFY_CAPACITY) tryPresize(n << 1); // 上面代码是链表转红黑树 else if ((b = tabAt(tab, index)) != null && b.hash >= 0) { synchronized (b) { if (tabAt(tab, index) == b) { TreeNode<K,V> hd = null, tl = null; for (Node<K,V> e = b; e != null; e = e.next) { TreeNode<K,V> p = new TreeNode<K,V>(e.hash, e.key, e.val, null, null); if ((p.prev = tl) == null) hd = p; else tl.next = p; tl = p; } setTabAt(tab, index, new TreeBin<K,V>(hd)); } } } }}
扩容
private final void tryPresize(int size) { // 这个size曾经是原先的两倍了 // 判断size是否超过最大容量。若不超过则 c 为 size 的 1.5 倍,再加 1,再往上取最近的 2 的 n 次方。 int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(size + (size >>> 1) + 1); int sc; while ((sc = sizeCtl) >= 0) { Node<K,V>[] tab = table; int n; if (tab == null || (n = tab.length) == 0) { // 上面代码和初始化代码相似 n = (sc > c) ? sc : c; if (U.compareAndSetInt(this, SIZECTL, sc, -1)) { try { if (table == tab) { @SuppressWarnings("unchecked") Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; table = nt; sc = n - (n >>> 2); } } finally { sizeCtl = sc; } } } // else if (c <= sc || n >= MAXIMUM_CAPACITY) break; else if (tab == table) { int rs = resizeStamp(n); // CAS操作将sizeCtl设置为 (rs << RESIZE_STAMP_SHIFT) + 2) if (U.compareAndSetInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) transfer(tab, null); } }}
transfer办法可能会多线程调用,
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) { // stride可了解为“步长”,其在单核下间接等于n,即原数组长度 // 在多核下,为 n>>>3/NCPU,最小值为16 // 心愿让每个CPU都尽可能解决的桶数一样多,如果桶很少,就默认一个线程解决16个桶 int n = tab.length, stride; if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE) stride = MIN_TRANSFER_STRIDE; // subdivide range // 如果nextTab为null,会先进行一次初始化。 // 可保障第一次个发动数据迁徙的线程调用此办法时,nextTab为null(具体可看可看此办法的调用处) 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和transferIndex都是concurrentmap中的属性 nextTable = nextTab; transferIndex = n; } int nextn = nextTab.length; // ForwardingNode——正在迁徙中的node,这个node的hash值是 MOVED // 当其余线程发现此node时,会跳过。起到占位作用 ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab); // advance为true,则阐明须要进行下个地位的迁徙 boolean advance = true; // 实现状态 boolean finishing = false; // to ensure sweep before committing nextTab // i是地位索引下标 // bound是以后线程能够解决的区间的最小边界 for (int i = 0, bound = 0;;) { Node<K,V> f; int fh; // 管制i的递加(从后向前) while (advance) { int nextIndex, nextBound; // 如果i-->=bound,或者实现状态为true,阐明迁徙工作曾经实现。会将advance批改为false // 第一次进入循环,个别会进入上面分支代码(i--无奈通过)。 if (--i >= bound || finishing) advance = false; // 当线程进入会选取最新的转移下标 // 如果 transferIndex <=0,意味着没有区间了,扩容可完结。 else if ((nextIndex = transferIndex) <= 0) { i = -1; advance = false; } // CAS批改 transferIndex(length-区间值)。 else if (U.compareAndSetInt (this, TRANSFERINDEX, nextIndex, nextBound = (nextIndex > stride ? nextIndex - stride : 0))) { // 获取以后线程可进来区间的最小下标 bound = nextBound; // i是以后线程能够解决的以后区间的最大下标 i = nextIndex - 1; advance = false; } } // 对i的状态进行判断? if (i < 0 || i >= n || i + n >= nextn) { int sc; if (finishing) { // 实现扩容的状况下,对相干属性进行更新 nextTable = null; table = nextTab; sizeCtl = (n << 1) - (n >>> 1); return; } // CAS操作将sizeCtl-1 // sizeCtl 在迁徙前会设置为 (rs << RESIZE_STAMP_SHIFT) + 2。而后,每有一个线程参加迁徙就会将 sizeCtl 加 1 // 这里去 -1 ,代表线程实现了属于本人的工作 if (U.compareAndSetInt(this, SIZECTL, sc = sizeCtl, sc - 1)) { // 工作实现,可return if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT) return; // 阐明 (sc - 2) == resizeStamp(n) << RESIZE_STAMP_SHIFT,即所有的迁徙工作已实现 finishing = advance = true; i = n; // recheck before commit } } // 原数组下标i处节点若为空,则放入ForwardingNode(占位) else if ((f = tabAt(tab, i)) == null) advance = casTabAt(tab, i, null, fwd); // 阐明被其余线程占位了。持续下一个地位 else if ((fh = f.hash) == MOVED) advance = true; // already processed else { // 到此处阐明,以后下标有理论值,并且未被占位解决。 // 对其加锁,筹备进行迁徙 synchronized (f) { if (tabAt(tab, i) == f) { // 将链表一分为二,并找到原链表中的lastRun,而后lastRun和其之后的节点进行迁徙 // lastRun之前的节点进行克隆后,分到两个链表中 Node<K,V> ln, hn; // 头结点hash>0,是链表,否则是红黑树 if (fh >= 0) { // 对原数组长度进行与运算 int runBit = fh & n; // Node<K,V> lastRun = f; // 遍历 for (Node<K,V> p = f.next; p != null; p = p.next) { // 与 计算 int b = p.hash & n; // 以后节点的计算结果和头节点不同 if (b != runBit) { runBit = b; lastRun = p; } } // 设置低位节点 if (runBit == 0) { ln = lastRun; hn = null; } // 设置高位节点 else { hn = lastRun; ln = null; } // 循环生成两个链表 for (Node<K,V> p = f; p != lastRun; p = p.next) { int ph = p.hash; K pk = p.key; V pv = p.val; if ((ph & n) == 0) ln = new Node<K,V>(ph, pk, pv, ln); else hn = new Node<K,V>(ph, pk, pv, hn); } // 低位链表放在新数组下标i处 setTabAt(nextTab, i, ln); // 高位链表放在新数组下标i+n处 setTabAt(nextTab, i + n, hn); // 原数组下标i处进行占位,示意处理完毕 setTabAt(tab, i, fwd); advance = true; } else if (f instanceof TreeBin) { // 树的解决...略过 TreeBin<K,V> t = (TreeBin<K,V>)f; TreeNode<K,V> lo = null, loTail = null; TreeNode<K,V> hi = null, hiTail = null; int lc = 0, hc = 0; for (Node<K,V> e = t.first; e != null; e = e.next) { int h = e.hash; TreeNode<K,V> p = new TreeNode<K,V> (h, e.key, e.val, null, null); if ((h & n) == 0) { if ((p.prev = loTail) == null) lo = p; else loTail.next = p; loTail = p; ++lc; } else { if ((p.prev = hiTail) == null) hi = p; else hiTail.next = p; hiTail = p; ++hc; } } ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) : (hc != 0) ? new TreeBin<K,V>(lo) : t; hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) : (lc != 0) ? new TreeBin<K,V>(hi) : t; setTabAt(nextTab, i, ln); setTabAt(nextTab, i + n, hn); setTabAt(tab, i, fwd); advance = true; } } } } }}