面试二、jdk1.8之concurrentHashMap

1、线程安全,使用场景:Session就是用concurrentHashMap实现的

2、数据结构

  jdk1.7版本,底层是segment数组,segment底层是table数组+链表,segment继承了ReentrantLock实现线程安全。可以理解为将一个大的table分成多个小的table来分别加锁

  jdk1.8版本,数据结构和hashMap一致,Table数组+ Node链表/TreeBin

3、简单说下jdk1.7版本,因已废弃不详细展开

  put():和hashMap基本一致,只是在第一步先hash定位segment位置,由于segment是线程安全的所以保证了自己是线程安全

  get():和hashMap基本一致,只是在第一步先hash定位segment位置

  获取size:会先不加锁进行三次计算大小如一致则返回;否则将所有segment加锁计算大小返回

4、着重看jdk1.8版本

  4.1、sizeCtl:volatile修饰线程可见,控制标识符,-1表示正在初始化,-n表示有n-1个线程正在进行扩容,0表示还没初始化,n表示下一次扩容大小

  4.2、RESIZE_STAMP_BITS:16,表示

  4.3、RESIZE_STAMP_SHIFT:32-RESIZE_STAMP_BITS,

  4.4、MOVED:-1,表示这是一个forwordNode节点

  4.5、TREEBIN:-2,表示这是一个treeBin节点

  4.6、MIN_TRANSFER_STRIDE:

  4.7、RESERVED:-3

  4.8、Node:和hashMap一样通过实现Map.Entry<K,V>,但是将val和next用volatile修饰设置成线程可见,同时不允许setValue操作,增加了find方法

 1 /**
 2      * Key-value entry.  This class is never exported out as a
 3      * user-mutable Map.Entry (i.e., one supporting setValue; see
 4      * MapEntry below), but can be used for read-only traversals used
 5      * in bulk tasks.  Subclasses of Node with a negative hash field
 6      * are special, and contain null keys and values (but are never
 7      * exported).  Otherwise, keys and vals are never null.
 8      */
 9     static class Node<K,V> implements Map.Entry<K,V> {
10         final int hash;
11         final K key;
12         volatile V val;
13         volatile Node<K,V> next;
14 
15         Node(int hash, K key, V val, Node<K,V> next) {
16             this.hash = hash;
17             this.key = key;
18             this.val = val;
19             this.next = next;
20         }
21 
22         public final K getKey()       { return key; }
23         public final V getValue()     { return val; }
24         public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }
25         public final String toString(){ return key + "=" + val; }
26         public final V setValue(V value) {
27             throw new UnsupportedOperationException();
28         }
29 
30         public final boolean equals(Object o) {
31             Object k, v, u; Map.Entry<?,?> e;
32             return ((o instanceof Map.Entry) &&
33                     (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
34                     (v = e.getValue()) != null &&
35                     (k == key || k.equals(key)) &&
36                     (v == (u = val) || v.equals(u)));
37         }
38 
39         /**
40          * Virtualized support for map.get(); overridden in subclasses.
41          */
42         Node<K,V> find(int h, Object k) {
43             Node<K,V> e = this;
44             if (k != null) {
45                 do {
46                     K ek;
47                     if (e.hash == h &&
48                         ((ek = e.key) == k || (ek != null && k.equals(ek))))
49                         return e;
50                 } while ((e = e.next) != null);
51             }
52             return null;
53         }
54     }

  4.9、treeNode:当链表过长时会转换为TreeNode,放入treeBin对象用来树化

 1     /**
 2      * Nodes for use in TreeBins
 3      */
 4     static final class TreeNode<K,V> extends Node<K,V> {
 5         TreeNode<K,V> parent;  // red-black tree links
 6         TreeNode<K,V> left;
 7         TreeNode<K,V> right;
 8         TreeNode<K,V> prev;    // needed to unlink next upon deletion
 9         boolean red;
10 
11         TreeNode(int hash, K key, V val, Node<K,V> next,
12                  TreeNode<K,V> parent) {
13             super(hash, key, val, next);
14             this.parent = parent;
15         }
16 
17         Node<K,V> find(int h, Object k) {
18             return findTreeNode(h, k, null);
19         }
20 
21         /**
22          * Returns the TreeNode (or null if not found) for the given key
23          * starting at given root.
24          */
25         final TreeNode<K,V> findTreeNode(int h, Object k, Class<?> kc) {
26             if (k != null) {
27                 TreeNode<K,V> p = this;
28                 do  {
29                     int ph, dir; K pk; TreeNode<K,V> q;
30                     TreeNode<K,V> pl = p.left, pr = p.right;
31                     if ((ph = p.hash) > h)
32                         p = pl;
33                     else if (ph < h)
34                         p = pr;
35                     else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
36                         return p;
37                     else if (pl == null)
38                         p = pr;
39                     else if (pr == null)
40                         p = pl;
41                     else if ((kc != null ||
42                               (kc = comparableClassFor(k)) != null) &&
43                              (dir = compareComparables(kc, k, pk)) != 0)
44                         p = (dir < 0) ? pl : pr;
45                     else if ((q = pr.findTreeNode(h, k, kc)) != null)
46                         return q;
47                     else
48                         p = pl;
49                 } while (p != null);
50             }
51             return null;
52         }
53     }

  4.10、treeBin:代码过多就放了,主要功能就是用来做树化处理

  4.11、ForwardingNode:hash值为MOVED:-1,此类的find方法查找的是nextTable

 1 /**
 2      * A node inserted at head of bins during transfer operations.
 3      */
 4     static final class ForwardingNode<K,V> extends Node<K,V> {
 5         final Node<K,V>[] nextTable;
 6         ForwardingNode(Node<K,V>[] tab) {
 7             super(MOVED, null, null, null);
 8             this.nextTable = tab;
 9         }
10 
11         Node<K,V> find(int h, Object k) {
12             // loop to avoid arbitrarily deep recursion on forwarding nodes
13             outer: for (Node<K,V>[] tab = nextTable;;) {
14                 Node<K,V> e; int n;
15                 if (k == null || tab == null || (n = tab.length) == 0 ||
16                     (e = tabAt(tab, (n - 1) & h)) == null)
17                     return null;
18                 for (;;) {
19                     int eh; K ek;
20                     if ((eh = e.hash) == h &&
21                         ((ek = e.key) == k || (ek != null && k.equals(ek))))
22                         return e;
23                     if (eh < 0) {
24                         if (e instanceof ForwardingNode) {
25                             tab = ((ForwardingNode<K,V>)e).nextTable;
26                             continue outer;
27                         }
28                         else
29                             return e.find(h, k);
30                     }
31                     if ((e = e.next) == null)
32                         return null;
33                 }
34             }
35         }
36     }

  4.12、get():先定位table位置,取首节点判断是否为当前要找的key,是则直接返回

        不是则判断节点hash是否小于0,小于0表示正在扩容或者为树结构,调用find()查找

        大于0则遍历链表节点查询对应key

 1 /**
 2      * Returns the value to which the specified key is mapped,
 3      * or {@code null} if this map contains no mapping for the key.
 4      *
 5      * <p>More formally, if this map contains a mapping from a key
 6      * {@code k} to a value {@code v} such that {@code key.equals(k)},
 7      * then this method returns {@code v}; otherwise it returns
 8      * {@code null}.  (There can be at most one such mapping.)
 9      *
10      * @throws NullPointerException if the specified key is null
11      */
12     public V get(Object key) {
13         Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
14         int h = spread(key.hashCode());
15         if ((tab = table) != null && (n = tab.length) > 0 &&
16             (e = tabAt(tab, (n - 1) & h)) != null) {
17             if ((eh = e.hash) == h) {
18                 if ((ek = e.key) == key || (ek != null && key.equals(ek)))
19                     return e.val;
20             }
21             else if (eh < 0)
22                 return (p = e.find(h, key)) != null ? p.val : null;
23             while ((e = e.next) != null) {
24                 if (e.hash == h &&
25                     ((ek = e.key) == key || (ek != null && key.equals(ek))))
26                     return e.val;
27             }
28         }
29         return null;
30     }

  4.13、put():注意:下面整个逻辑加了死循环,一直到put成功后break

        ---for循环开始---

        25行:如果map为空,初始化

        27行:定位table位置,如果首节点为空则CAS插入break

        32行:如果节点正在扩容调用helpTranfer帮助扩容

        36行:将首节点加锁,

        38行:如果首节点的hash大于等于0则为链表结构,遍历如有相等节点则覆盖值,没有则一直遍历到最后一个插入

        56行:如果小于0(树结构首节点的hash值为TREEBIN:-2)则为树结构,调用TreeBin.putTreeVal()插入

        71行:如果当前链表节点数量bigCount大于树化阀值,调用treefyBin(),break

        ---for循环结束---

        79行:最后调用addCount,计算map到size

 1 /**
 2      * Maps the specified key to the specified value in this table.
 3      * Neither the key nor the value can be null.
 4      *
 5      * <p>The value can be retrieved by calling the {@code get} method
 6      * with a key that is equal to the original key.
 7      *
 8      * @param key key with which the specified value is to be associated
 9      * @param value value to be associated with the specified key
10      * @return the previous value associated with {@code key}, or
11      *         {@code null} if there was no mapping for {@code key}
12      * @throws NullPointerException if the specified key or value is null
13      */
14     public V put(K key, V value) {
15         return putVal(key, value, false);
16     }
17 
18     /** Implementation for put and putIfAbsent */
19     final V putVal(K key, V value, boolean onlyIfAbsent) {
20         if (key == null || value == null) throw new NullPointerException();
21         int hash = spread(key.hashCode());
22         int binCount = 0;
23         for (Node<K,V>[] tab = table;;) {
24             Node<K,V> f; int n, i, fh;
25             if (tab == null || (n = tab.length) == 0)
26                 tab = initTable();
27             else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
28                 if (casTabAt(tab, i, null,
29                              new Node<K,V>(hash, key, value, null)))
30                     break;                   // no lock when adding to empty bin
31             }
32             else if ((fh = f.hash) == MOVED)
33                 tab = helpTransfer(tab, f);
34             else {
35                 V oldVal = null;
36                 synchronized (f) {
37                     if (tabAt(tab, i) == f) {
38                         if (fh >= 0) {
39                             binCount = 1;
40                             for (Node<K,V> e = f;; ++binCount) {
41                                 K ek;
42                                 if (e.hash == hash &&
43                                     ((ek = e.key) == key ||
44                                      (ek != null && key.equals(ek)))) {
45                                     oldVal = e.val;
46                                     if (!onlyIfAbsent)
47                                         e.val = value;
48                                     break;
49                                 }
50                                 Node<K,V> pred = e;
51                                 if ((e = e.next) == null) {
52                                     pred.next = new Node<K,V>(hash, key,
53                                                               value, null);
54                                     break;
55                                 }
56                             }
57                         }
58                         else if (f instanceof TreeBin) {
59                             Node<K,V> p;
60                             binCount = 2;
61                             if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
62                                                            value)) != null) {
63                                 oldVal = p.val;
64                                 if (!onlyIfAbsent)
65                                     p.val = value;
66                             }
67                         }
68                     }
69                 }
70                 if (binCount != 0) {
71                     if (binCount >= TREEIFY_THRESHOLD)
72                         treeifyBin(tab, i);
73                     if (oldVal != null)
74                         return oldVal;
75                     break;
76                 }
77             }
78         }
79         addCount(1L, binCount);
80         return null;
81     }

  4.14、扩容:如果当前节点标记为ForwardingNode,则帮助扩容

        40行:如果nextTable是空,则初始化一个nextTable大小是原table的两倍(>>2)

        53行:初始化一个ForwardingNode

        56行:进入循环

        58行:此循环是用来控制i的值,易遍历原table的所有节点,

            如果原来table的长度是0,设置i为-1进入75行,直接标记当前节点完成

            否则i=tranferIndex-1(即原table最后一个)

        75行:如果原先table都已遍历完了,finshing标记是ture,则将nextTable赋给table,设置下次扩容阀值为原先n的1.5倍(即当前的0.75,因为当前n是原先n的2倍),扩容结束

                    否则将sizeCtl减1(因为进入地方说明此线程帮助扩容已经完成,要将在进行扩容的线程数减1),设置finishing为true

        90行:如果原table此位置的首节点是空,则将forwardingNode放入

        92行:如果原table此位置的首节点是forwardingNode,跳过

        95行:将首节点加锁

        98行:如果是Node链表(fh>=0),初始化两个Node链表ln(原位置)和hn(新位置),重新计算原Node链表上每个节点在新table上的位置(ph&n)

            如果是0说明在原位置i,如果不是0说明不在原位置将其放到i+n

        128行:如果是是树结构树结构(首节点的hash值为TREEBIN:-2),同理初始化两个TreeNode链表后面操作和上面操作一样,如果新链表不需要树化则转换为Node链表将链表       

            插入table,否则树化插入table

  1     /**
  2      * Helps transfer if a resize is in progress.
  3      */
  4     final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
  5         Node<K,V>[] nextTab; int sc;
  6         if (tab != null && (f instanceof ForwardingNode) &&
  7             (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
  8             int rs = resizeStamp(tab.length);
  9             while (nextTab == nextTable && table == tab &&
 10                    (sc = sizeCtl) < 0) {
 11                 if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
 12                     sc == rs + MAX_RESIZERS || transferIndex <= 0)
 13                     break;
 14                 if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
 15                     transfer(tab, nextTab);
 16                     break;
 17                 }
 18             }
 19             return nextTab;
 20         }
 21         return table;
 22     }
 23 
 24     /**
 25      * Returns the stamp bits for resizing a table of size n.
 26      * Must be negative when shifted left by RESIZE_STAMP_SHIFT.
 27      */
 28     static final int resizeStamp(int n) {
 29         return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
 30     }
 31 
 32     /**
 33      * Moves and/or copies the nodes in each bin to new table. See
 34      * above for explanation.
 35      */
 36     private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
 37         int n = tab.length, stride;
 38         if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
 39             stride = MIN_TRANSFER_STRIDE; // subdivide range
 40         if (nextTab == null) {            // initiating
 41             try {
 42                 @SuppressWarnings("unchecked")
 43                 Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
 44                 nextTab = nt;
 45             } catch (Throwable ex) {      // try to cope with OOME
 46                 sizeCtl = Integer.MAX_VALUE;
 47                 return;
 48             }
 49             nextTable = nextTab;
 50             transferIndex = n;
 51         }
 52         int nextn = nextTab.length;
 53         ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
 54         boolean advance = true;
 55         boolean finishing = false; // to ensure sweep before committing nextTab
 56         for (int i = 0, bound = 0;;) {
 57             Node<K,V> f; int fh;
 58             while (advance) {
 59                 int nextIndex, nextBound;
 60                 if (--i >= bound || finishing)
 61                     advance = false;
 62                 else if ((nextIndex = transferIndex) <= 0) {
 63                     i = -1;
 64                     advance = false;
 65                 }
 66                 else if (U.compareAndSwapInt
 67                          (this, TRANSFERINDEX, nextIndex,
 68                           nextBound = (nextIndex > stride ?
 69                                        nextIndex - stride : 0))) {
 70                     bound = nextBound;
 71                     i = nextIndex - 1;
 72                     advance = false;
 73                 }
 74             }
 75             if (i < 0 || i >= n || i + n >= nextn) {
 76                 int sc;
 77                 if (finishing) {
 78                     nextTable = null;
 79                     table = nextTab;
 80                     sizeCtl = (n << 1) - (n >>> 1);
 81                     return;
 82                 }
 83                 if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
 84                     if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
 85                         return;
 86                     finishing = advance = true;
 87                     i = n; // recheck before commit
 88                 }
 89             }
 90             else if ((f = tabAt(tab, i)) == null)
 91                 advance = casTabAt(tab, i, null, fwd);
 92             else if ((fh = f.hash) == MOVED)
 93                 advance = true; // already processed
 94             else {
 95                 synchronized (f) {
 96                     if (tabAt(tab, i) == f) {
 97                         Node<K,V> ln, hn;
 98                         if (fh >= 0) {
 99                             int runBit = fh & n;
100                             Node<K,V> lastRun = f;
101                             for (Node<K,V> p = f.next; p != null; p = p.next) {
102                                 int b = p.hash & n;
103                                 if (b != runBit) {
104                                     runBit = b;
105                                     lastRun = p;
106                                 }
107                             }
108                             if (runBit == 0) {
109                                 ln = lastRun;
110                                 hn = null;
111                             }
112                             else {
113                                 hn = lastRun;
114                                 ln = null;
115                             }
116                             for (Node<K,V> p = f; p != lastRun; p = p.next) {
117                                 int ph = p.hash; K pk = p.key; V pv = p.val;
118                                 if ((ph & n) == 0)
119                                     ln = new Node<K,V>(ph, pk, pv, ln);
120                                 else
121                                     hn = new Node<K,V>(ph, pk, pv, hn);
122                             }
123                             setTabAt(nextTab, i, ln);
124                             setTabAt(nextTab, i + n, hn);
125                             setTabAt(tab, i, fwd);
126                             advance = true;
127                         }
128                         else if (f instanceof TreeBin) {
129                             TreeBin<K,V> t = (TreeBin<K,V>)f;
130                             TreeNode<K,V> lo = null, loTail = null;
131                             TreeNode<K,V> hi = null, hiTail = null;
132                             int lc = 0, hc = 0;
133                             for (Node<K,V> e = t.first; e != null; e = e.next) {
134                                 int h = e.hash;
135                                 TreeNode<K,V> p = new TreeNode<K,V>
136                                     (h, e.key, e.val, null, null);
137                                 if ((h & n) == 0) {
138                                     if ((p.prev = loTail) == null)
139                                         lo = p;
140                                     else
141                                         loTail.next = p;
142                                     loTail = p;
143                                     ++lc;
144                                 }
145                                 else {
146                                     if ((p.prev = hiTail) == null)
147                                         hi = p;
148                                     else
149                                         hiTail.next = p;
150                                     hiTail = p;
151                                     ++hc;
152                                 }
153                             }
154                             ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
155                                 (hc != 0) ? new TreeBin<K,V>(lo) : t;
156                             hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
157                                 (lc != 0) ? new TreeBin<K,V>(hi) : t;
158                             setTabAt(nextTab, i, ln);
159                             setTabAt(nextTab, i + n, hn);
160                             setTabAt(tab, i, fwd);
161                             advance = true;
162                         }
163                     }
164                 }
165             }
166         }
167     }

  4.15、三个原子操作方法,对指定节点进行原子操作,保证线程安全(链表里的操作通过synchronized锁实现)

      2行:查找table指定坐标的Node

      6行:cas操作设置table指定坐标Node

      11行:设置table指定坐标Node

 1     @SuppressWarnings("unchecked")
 2     static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
 3         return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
 4     }
 5 
 6     static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
 7                                         Node<K,V> c, Node<K,V> v) {
 8         return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
 9     }
10 
11     static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
12         U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
13     }

 

posted on 2021-08-25 18:31  Iversonstear  阅读(63)  评论(0编辑  收藏  举报

导航