(3)数组不为空了,则哈希映射数组下标(f = tabAt(tab, i = (n - 1) & hash)),从主内存中获取最新的节点,若为空则说明没有发生哈希冲突,cas设置新节点到对应位置,设置失败可能因为有其他线程竞争设置了,则重新循环判断 。(long)i = 0,判断其是普通节点,即链表,遍历链表,有key相同的节点则判断是替换还是直接结束,若是新增节点,则以尾插法加到链表尾部,在遍历链表的过程中自增,记录链表的长度,后面看是否需要转为红黑树 。(java7新增节点用的是头插法,用的也是头插法,并发情况下容易造成循环链表死循环,后来java8就都用尾插法了) 。
(7)fh < 0 && f判断是红黑树,返回值为null是新增节点,不为null则返回值是树中已存在的节点,判断是否需要替换 。
(8)树化判断,开始赋值为0,若新节点加到了链表中,会在遍历链表的过程中累加记录链表的长度,若新节点加到了红黑树中,赋值为2;>= ,说明链表的节点达到树化的阈值8个,则执行 。
达到树化阈值不一定就链表转为红黑树,若数组的长度小于=64,需要先扩容(n ==64,则锁住当前位置占位节点,开始树化 。
private final void treeifyBin(Node
(9)不是替换元素,需要最后执行(1L, ),元素个数+1 。
三、元素计数
较为复杂单独拎出来讨论 。一个简单的元素个数加减,如果让你来实现这个功能该如何做,一个修饰的变量,然后cas加减?在竞争激烈的情况,cas自旋可能会成为性能瓶颈,某些线程会因为cas计数失败而长时间自旋 。
文章插图
Doug Lea是怎么做的呢?基础变量()+数组([])辅助计数 。
作者的思路就是尽量避免竞争,cas修改成功就不会再去修改[],修改失败也不会自旋,以哈希映射的方式找到[]对应位置的格子cas计数,依然失败就多次重复哈希映射找其他空闲格子,还失败就扩容[],扩容之后都竞争不过其他线程,此时就进入自旋重复哈希映射,直到修改成功,虽然最终可能也会陷入不断自旋重试的情况,但是多个线程抢多个资源和多个线程抢一个资源相比,性能明显会好很多 。详情看代码:
private final void addCount(long x, int check) {CounterCell[] as; long b, s;// counterCells!=null,则直接在counterCells里加元素个数,// counterCells=null,则尝试修改baseCount,失败则修改counterCells 。if ((as = counterCells) != null ||!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {CounterCell a; long v; int m;// 初始化乐观的认为true即没有竞争boolean uncontended = true;// ThreadLocalRandom.getProbe() 相当于当前线程的hash值if (as == null || (m = as.length - 1)
- 中国5个最有特色的摩天轮第四个独一无二第五个世界最高 中国之最摩天轮
- 徐静雨:现役中库里排第二杜兰特只能排第三,这排名非常不严谨 徐静雨各个位置历史之最
- 火影忍者十大瞳术排行榜:轮墓·边狱只能排第二 世界十大瞳术
- 湖二师学子三下乡:素描画中国 中国之最简单画法
- 塞纳当选F1过去四十年最快车手!舒马赫、汉密尔顿分列二三 f1历史之最车手排位
- H5云商城源码全新UI|支持易支付v1.0【个人开发全网首发】
- 二 自动化测试 - RFT系列教程4: RFT是如何识别控件的 对象库与脚本
- 清朝十二帝:揭光绪帝和他的四个男人之谜
- 来涨姿势了,可能有用并使你才识渊博的冷知识来袭 二 跳远的吉尼斯记录
- 七十二景