代码改变世界

ConcurrentHashMap实现原理

2022-12-12 15:18  杭伟  阅读(23)  评论(0编辑  收藏  举报

jdk1.7分段锁:

Segment类(小型HashMap),继承ReentrantLock

 put简要流程:

先根据key,算出Segment数组的下标index

加锁

生成Entry对象,并添加到Segment的数组或链表上(和HashMap相同)

segments[index].put(key,value);

释放锁

ConcurrentHashMap源码

构造函数:

构造函数中主要生成segement数组,并将Segment对象放入segment数组的第一个位置。

segment数组大小为什么没有直接从参数获取而是取了一个>=传参的2^n的数?

因为计算数组下标时,使用的是hashcode和&table.length-1(与运算) ,在之前HashMap章节有说明这个问题,这里不再详述。

 

 

 

jdk1.8 synchronized + CAS

 1.8ConcurrentHashMap采用和HashMap一样的结构:

 1.7使用HashEntry,1.8改为Node

 1.8put简要流程:

1、根据 key 计算出 hashcode,然后开始遍历 table;
2、判断是否需要初始化;
3、f 即为当前 key 定位出的 Node,如果为空表示当前位置可以写入数据,利用 CAS 尝试写入,失败则自旋保证成功。
4、如果当前位置的 hashcode == MOVED == -1,则需要进行扩容。
5、如果都不满足,则利用 synchronized 锁写入数据。
6、如果数量大于 TREEIFY_THRESHOLD 则要转换为红黑树。

 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());
 4         int binCount = 0;
 5         for (Node<K,V>[] tab = table;;) {//1
 6             Node<K,V> f; int n, i, fh;
 7             if (tab == null || (n = tab.length) == 0)//2
 8                 tab = initTable();
 9             else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {//3
10                 if (casTabAt(tab, i, null,
11                              new Node<K,V>(hash, key, value, null)))
12                     break;                   // no lock when adding to empty bin
13             }
14             else if ((fh = f.hash) == MOVED)//4
15                 tab = helpTransfer(tab, f);
16             else {
17                 V oldVal = null;
18                 synchronized (f) {//5
19                     if (tabAt(tab, i) == f) {
20                         if (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)))) {
27                                     oldVal = e.val;
28                                     if (!onlyIfAbsent)
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,
35                                                               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)//6
54                         treeifyBin(tab, i);
55                     if (oldVal != null)
56                         return oldVal;
57                     break;
58                 }
59             }
60         }
61         addCount(1L, binCount);
62         return null;
63     }

可以看到1.8取消了1.7中使用的ReentrantLock改为了synchronized,也看出1.8jdk对synchronized优化后的信心。