• ConcurrentHashMap--addCount()


    ===

    1. package leetcode0606.JUC.ConcurrentHashMap;
    2. import java.util.concurrent.ConcurrentHashMap;
    3. import java.util.concurrent.ThreadLocalRandom;
    4. public class _addCount {
    5. addCount(1L, binCount);
    6. private final void addCount(long x, int check) {
    7. /*
    8. check
    9. >=1 当前桶位 链表的长度 或者 发生冲突的位置
    10. =0 当前桶位的 头节点为null
    11. 2 当前桶位为红黑树
    12. */
    13. /*
    14. LongAdder
    15. */
    16. // s 当前map.table中的元素数量
    17. // b 表示 LongAdder 中的base值
    18. // as 表示cells数组
    19. ConcurrentHashMap.CounterCell[] as; long b, s;
    20. /*
    21. 条件一: as = counterCells) != null
    22. true 表示cells数组已经初始化,当前线程使用hash去寻址找到合适的cell,去累加数据
    23. false 走一下个跳进
    24. =====================================================================================================
    25. 条件二: !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)
    26. true 说明cells数组未初始化,该线程只能去写basecount,结果失败了,即与其它线程发生了冲突,当前线程尝试去创建cells数组
    27. false 说明写basecount成功,不需要创建cells数组,直接退出。
    28. */
    29. if ((as = counterCells) != null ||
    30. !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
    31. /*
    32. a 当前哈希寻址命中的cell
    33. v 写cell的期望值
    34. m 当前cells数组的长度
    35. */
    36. ConcurrentHashMap.CounterCell a; long v; int m;
    37. // true 未竞争
    38. boolean uncontended = true;
    39. /*
    40. 前置条件: true 表示cells数组已经初始化,当前线程使用hash去寻址找到合适的cell,去累加数据
    41. true 说明cells数组未初始化,该线程只能去写basecount,结果失败了,即与其它线程发生了冲突,当前线程尝试去创建cells数组
    42. ----------------------------------------------------------------------------------------------------
    43. 条件一:as == null || (m = as.length - 1) < 0
    44. true cells数组未初始化,且写base失败,进入 fullAddCount() 去扩容或者重试
    45. 条件二:a = as[ThreadLocalRandom.getProbe() & m]) == null
    46. true cells已经初始化了,且哈希寻址的cell为null,需要创建新的cell节点,放入cells数组中。
    47. 条件三:!(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
    48. true 当前cell单元格不为null,有值了,去进行CAS累加操作,若竞争失败,进入以下方法,进行多次尝试,若仍然失败,进行扩容。
    49. */
    50. if (as == null || (m = as.length - 1) < 0 ||
    51. (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
    52. !(uncontended =
    53. U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
    54. // 与 LongAdder 的核心方法一致
    55. fullAddCount(x, uncontended);
    56. return;
    57. }
    58. if (check <= 1)
    59. return;
    60. s = sumCount();
    61. }
    62. ============================================================================================
    63. ============================================================================================
    64. // 扩容
    65. // check >=0 表示是put()方法调用的addCount();
    66. // check <0 表示是remove()方法调用的addCount();
    67. if (check >= 0) {
    68. // tab 老数组
    69. // nt (nexttable) 扩容之后的数组
    70. // n table的长度
    71. // sc sizeCtl的临时变量
    72. ConcurrentHashMap.Node[] tab, nt; int n, sc;
    73. // sizeCtl < 0 大概率是-1
    74. // -1 表示table正在初始化(有其它线程在创建table数组),当前线程需要自旋等
    75. //======================table数组正在扩容,高16位表示 扩容的标识戳 低16位表示 (1+nThread) ,参与并发扩容的线程数量。======================
    76. // sizeCtl = 0
    77. // 表示创建table数组时,使用 DEFAULT_CAPACITY大小
    78. //
    79. // sizeCtl > 0
    80. // 如果table未初始化,表示初始化大小 ,如果调用有参构造器,其中有这么一句 this.sizeCtl = cap;
    81. // 如果调用无参的那么sizeCtl = 0,走默认长度 -----int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
    82. // =======================如果已经初始化,表示下次扩容时的 触发条件(阈值)=================================
    83. // s 当前map.table中的元素数量
    84. /*
    85. 条件一:s >= (long)(sc = sizeCtl)
    86. true 当前sizeCtl是一个负数,即正在扩容中
    87. 当前sizeCtl是一个整数,表示这次扩容的阈值,要扩容了
    88. false 未达到扩容阈值,选择不扩容
    89. 条件二,三: (tab = table) != null && (n = tab.length) < MAXIMUM_CAPACITY 恒成立
    90. */
    91. while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
    92. (n = tab.length) < MAXIMUM_CAPACITY) {
    93. // 拿到扩容唯一标识戳
    94. // 假如从 16到32 ,那么 rs = 1000 0000 0001 1011
    95. int rs = resizeStamp(n);
    96. /*
    97. 当前table正在扩容,理论上当前线程要协助map扩容
    98. */
    99. if (sc < 0) {
    100. /*
    101. 条件一:(sc >>> RESIZE_STAMP_SHIFT) != rs 检查标尺戳使得否为对应的扩容
    102. 条件二: sc == (rs << 16) + 1
    103. true 标识扩容完毕了,当前线程不需要再参与了
    104. false 还在进行中,可以参与
    105. 条件三: sc == (rs << 16)+ MAX_RESIZERS .....
    106. 条件四: (nt = nextTable) == null
    107. true 扩容结束
    108. */
    109. if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
    110. sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
    111. transferIndex <= 0)
    112. break;
    113. // sc 低16位是 (1+nThread)
    114. // true 参与并发扩容的线程+1
    115. // false CAS失败,参与竞争的线程比较多,然后自旋
    116. if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
    117. // 协助扩容线程
    118. transfer(tab, nt);
    119. }
    120. /*
    121. 当前table
    122. // 高16位表示 扩容的标识戳 低16位表示 (1+nThread)
    123. // 1000 0000 0001 1011 , 0000 0000 0000 0010
    124. // +2说明当前线程是第一个触发扩容的线程,干的活多一些,修改完sc后你会发现它变成了一个负数,此操作也是在多线程的场景下进行的,当CAS操作失败后
    125. // 进入自旋,走 sc<0 的分支
    126. */
    127. else if (U.compareAndSwapInt(this, SIZECTL, sc,
    128. (rs << RESIZE_STAMP_SHIFT) + 2))
    129. // 触发扩容线程
    130. transfer(tab, null);
    131. s = sumCount();
    132. }
    133. }
    134. }
    135. }

  • 相关阅读:
    Netty 学习(六):创建 NioEventLoopGroup 的核心源码说明
    驱动开发练习,platform实现如下功能
    【开卷数据结构 】什么是二叉排序树(BST)?
    linux系统连接远程mysql数据库失败,意想不到的错误
    【Python】【Fintech】用Python和蒙特卡洛法预测投资组合未来收益
    点与线段的关系
    基于JavaWeb技术的在线考试系统设计与实现
    libevent之evbuffer
    【学习笔记】 字符串基础 : 后缀自动机(基础篇)
    视频格式转换avi格式怎么弄?分享视频转换方法
  • 原文地址:https://blog.csdn.net/weixin_51676493/article/details/126599468