【源码解析】ConcurrentHashMap

阳光穿透心脏的1/2处 2022-09-11 15:20 386阅读 0赞

废话不多说,Just show me your code

常量、变量:

常量:

  1. 1. 数组容量:
  2. private static final int MAXIMUM_CAPACITY = 1 << 30;
  3. private static final int DEFAULT_CAPACITY = 16;
  4. 2. 并发级别,桶位比这个小,按照这个来init
  5. private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
  6. 3. 负载因子,JDK1.8 ConcurrentHashMap 是固定值
  7. private static final float LOAD_FACTOR = 0.75f;
  8. 4. 树化、树的退化阈值:
  9. static final int TREEIFY_THRESHOLD = 8;
  10. static final int MIN_TREEIFY_CAPACITY = 64;
  11. static final int UNTREEIFY_THRESHOLD = 6;
  12. 5. 线程迁移数据最小步长,控制线程迁移任务最小区间一个值
  13. private static final int MIN_TRANSFER_STRIDE = 16;
  14. private static int RESIZE_STAMP_BITS = 16;
  15. private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
  16. private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
  17. 6. 节点状态
  18. static final int MOVED = -1; // hash for forwarding nodes
  19. static final int TREEBIN = -2; // hash for roots of trees
  20. static final int HASH_BITS = 0x7fffffff; // xx^HASH_BITS 维护值变为正数

变量:

  1. 1. normal
  2. transient volatile Node<K,V>[] table;
  3. private transient volatile Node<K,V>[] nextTable;
  4. 2. 并发
  5. private transient volatile long baseCount; // +所有cells
  6. private transient volatile int sizeCtl; // >=0 表阈值、 <0 在迁移
  7. private transient volatile int transferIndex;
  8. private transient volatile CounterCell[] counterCells;

构造方法:

一共5个,其他都是套娃
在这里插入图片描述
在这里插入图片描述

还有其他的一些小方法:tableSizeFor、spread都是HashMap中见过的

get方法:

在这里插入图片描述

关于find方法

自己在思考的时候遇到了一些错误,get的时候怎么确定会进入到哪个方法?

  1. put的时候已经确定了是FWD还是TreeBin,
  2. 在扩容时,当前桶挪完在桶位上放置FWD节点,其他线程定位到这个桶位,会调用FWD的find方法。
  3. 非扩容时,不会调用FWD的find方法。

在这里插入图片描述

在这里插入图片描述

put方法

在这里插入图片描述

在这里插入图片描述
链表情况:

  1. fh >= 0:链表
  2. fh = -1FWD
  3. fh = -2TreeBin节点

在这里插入图片描述

TreeBin节点情况:
在这里插入图片描述

最后:
检查是否需要树化 以及有没有发生替换操作
在这里插入图片描述

addCount方法:

类比HashMap 的resize方法

1.统计当前table一共有多少数据
2.判断是否达到扩容阈值标准,触发扩容。

remove方法:

  1. public V remove(Object key) {
  2. return replaceNode(key, null, null);
  3. }

replaceNode:

  1. final V replaceNode(Object key, V value, Object cv) {
  2. int hash = spread(key.hashCode());
  3. for (Node<K,V>[] tab = table;;) {
  4. Node<K,V> f; int n, i, fh;
  5. // CASE1:表空、桶空 =》 break
  6. if (tab == null || (n = tab.length) == 0 || (f = tabAt(tab, i = (n - 1) & hash)) == null)
  7. break;
  8. // CASE2:正在搬移,去帮忙
  9. else if ((fh = f.hash) == MOVED)
  10. tab = helpTransfer(tab, f);
  11. // CASE3: 链表或者红黑树TreeBin节点
  12. else {
  13. V oldVal = null; // 检查是否发生替换
  14. boolean validated = false; // 校验标记,是否有确定为链表或者红黑树TreeBin节点
  15. // 经典的double check
  16. synchronized (f) {
  17. if (tabAt(tab, i) == f) {
  18. if (fh >= 0) {
  19. // 链
  20. validated = true;
  21. for (Node<K,V> e = f, pred = null;;) {
  22. K ek;
  23. // hash和key都相同 确定是需要replace 的节点
  24. if (e.hash == hash && ((ek = e.key) == key ||
  25. (ek != null && key.equals(ek)))) {
  26. V ev = e.val;
  27. //条件一:cv == null true->替换的值为null 那么就是一个删除操作
  28. //条件二:cv == ev || (ev != null && cv.equals(ev)) 那么是一个替换操作
  29. if (cv == null || cv == ev || (ev != null && cv.equals(ev))) {
  30. oldVal = ev;
  31. // 1、替换操作
  32. if (value != null) e.val = value;
  33. // 2、要找的值在中间,移动next指针
  34. else if (pred != null) pred.next = e.next;
  35. // 3、repalce在头节点,桶位设置为头结点的下一个节点。
  36. else setTabAt(tab, i, e.next);
  37. }
  38. break;
  39. }
  40. pred = e;
  41. if ((e = e.next) == null)
  42. break;
  43. }
  44. }
  45. //
  46. else if (f instanceof TreeBin) {
  47. validated = true;
  48. TreeBin<K,V> t = (TreeBin<K,V>)f;
  49. TreeNode<K,V> r, p;
  50. if ((r = t.root) != null && (p = r.findTreeNode(hash, key, null)) != null) {
  51. // 在树上找到了要找的节点
  52. V pv = p.val;
  53. if (cv == null || cv == pv ||(pv != null && cv.equals(pv))) {
  54. oldVal = pv;
  55. // 1、替换
  56. if (value != null) p.val = value;
  57. // 2、删除
  58. else if (t.removeTreeNode(p))
  59. setTabAt(tab, i, untreeify(t.first));
  60. }
  61. }
  62. }
  63. }
  64. }
  65. if (validated) {
  66. if (oldVal != null) {
  67. //替换的值 为null,说明当前是一次删除操作
  68. // oldVal !=null 成立,说明删除成功,更新当前元素个数计数器。
  69. if (value == null) addCount(-1L, -1);
  70. return oldVal;
  71. }
  72. break;
  73. }
  74. }
  75. }
  76. return null;
  77. }

发表评论

表情:
评论列表 (有 0 条评论,386人围观)

还没有评论,来说两句吧...

相关阅读