java版本11.0.1,感觉写得太水了,等心情好的时候再重新编辑一下。

LongAdder中的核心逻辑主要由java.util.concurrent.atomic.Striped64维护,作为Striped64的继承类LongAdder定义了(LongAccumulator、DoubleAdder、DoubleAccumulator...)一些外围逻辑

    /**
* Cell(单元)表,不为null时大小为2的幂
*/
transient volatile Cell[] cells; /**
* 基值,主要用在没有竞争访问时使用, 也会用在竞争创建cells失败时的备选方案。CAS更新
*/
transient volatile long base; /**
* cells的自旋锁,在创建/扩容cells时会用到
*/
transient volatile int cellsBusy;

sum()遍历cells累加和base,reset()遍历cells和base赋值0,sumThenReset()遍历cells和base用CAS操作累加并赋值0。比较简单就这么概括一下。

主要方法LongAdder#add

  public void add(long x) {
Cell[] cs; long b, v; int m; Cell c;
/**
* 当一开始没有竞争调用时,CAS操作base值累加.当开始出现竞争时开始走下面的逻辑,不再累加base
*/
if ((cs = cells) != null || !casBase(b = base, b + x)) {
/**
* 设置“无竞争”标识
*/
boolean uncontended = true;
/**
* 当cells未初始化时继续以下判断逻辑,否则调用longAccumulate()
*/
if (cs == null || (m = cs.length - 1) < 0 ||
/**
* 根据访问线程的特征值(probeValue)获取cells中访问线程对应的cell. cell未初始化时调用longAccumulate()
* 线程特征值和cells.length的&操作确保cs[getProbe()&m]不会越界
*/
(c = cs[getProbe() & m]) == null ||
/**
* 对访问线程对应的cell的值CAS操作,并把执行结果赋值uncontended.若CAS操作失败则调用longAccumulate()
*/
!(uncontended = c.cas(v = c.value, v + x))) { longAccumulate(x, null, uncontended);
}
}
}

核心逻辑藏在了Striped64#longAccumulate,稍微花时间瞄了两眼。

  /**
* 处理涉及到初始化、扩容、创建及碰撞更新cell的情况.
*
* @param x 运算值
* @param fn 运算操作,null时为加法
* @param wasUncontended 调用该方法前CAS操作的结果,CAS操作失败则为false
*/
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
/**
* 线程特征值
*/
int h;
/**
* 当调用线程是第一次调用longAccumulate()时,赋值线程的特征值
*/
if ((h = getProbe()) == 0) {
ThreadLocalRandom.current();
h = getProbe();
wasUncontended = true;
}
/**
* 表示对cell的获取是否与其他线程碰撞, 用来判断cells是否需要扩容
*/
boolean collide = false;
/**
* 未获取到cellsBusy时则自旋
*/
done: for (;;) {
Striped64.Cell[] cs; Striped64.Cell c; int n; long v;
/**
* 当cells已存在并且不为空时
*/
if ((cs = cells) != null && (n = cs.length) > 0) {
/**
* 当访问线程对应的cell尚未存在时,新增Cell(x)
*/
if ((c = cs[(n - 1) & h]) == null) {
/**
* 尝试获取自旋锁
*/
if (cellsBusy == 0) {
/**
* (乐观)创建Cell
*/
Striped64.Cell r = new Striped64.Cell(x);
/**
* 尝试获取自旋锁
*/
if (cellsBusy == 0 && casCellsBusy()) {
try {
Striped64.Cell[] rs; int m, j;
/**
* 在持有cellsBusy锁的情况下再次检查访问线程对应的cell是否已存在
*/
if ((rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
/**
* 新增Cell并且跳出自旋
*/
rs[j] = r;
break done;
}
} finally {
cellsBusy = 0;
}
/**
* 新增Cell失败,自旋
*/
continue;
}
}
/**
* 如果尝试获取自旋锁失败,说明已有其他线程占用了该Cell,
* 之后为减少碰撞会调用advanceProbe()
*/
collide = false;
}
/**
* 如果先前的cs[getProbe()&m]的CAS累加操作失败, 则wasUncontended赋值true
*/
else if (!wasUncontended)
wasUncontended = true;
/**
* 对cell执行CAS操作,成功则方法结束,失败则自旋
*/
else if (c.cas(v = c.value,
(fn == null) ? v + x : fn.applyAsLong(v, x)))
break;
/**
* 当cells大小大于逻辑cpu数,不扩容
*/
else if (n >= NCPU || cells != cs)
collide = false;
/**
* 通过collide是否碰撞判断是否执行下面的扩容逻辑
* collide==false时则自旋
*/
else if (!collide)
collide = true;
/**
* 执行到这里说明需要扩容
* 尝试获取cellsBusy锁扩容
*/
else if (cellsBusy == 0 && casCellsBusy()) {
try {
if (cells == cs) // Expand table unless stale
cells = Arrays.copyOf(cs, n << 1);
} finally {
cellsBusy = 0;
}
collide = false;
continue;
}
/**
* 这是为了重新计算访问线程的特征值(advanceProbe(h))后自旋,减少碰撞
*/
h = advanceProbe(h);
}
/**
* 当cells没有正在初始化/扩容(cellsBusy == 0)并且cells未被创建(cells == cs)时,则设置cells的自旋锁cellBusy,开始创建cells对象
*/
else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
try {
/**
* 初始化cells和访问线程对应的Cell对象
*/
if (cells == cs) {
Striped64.Cell[] rs = new Striped64.Cell[2];
rs[h & 1] = new Striped64.Cell(x);
cells = rs;
break done;
}
} finally {
/**
* cells创建完成后释放cellBusy锁
*/
cellsBusy = 0;
}
}
/**
* 以上判断条件失败,则走备选逻辑:CAS操作运算base值(计算sum时会加上base值)
*/
else if (casBase(v = base,
(fn == null) ? v + x : fn.applyAsLong(v, x)))
break done;
}
}

懒得画流程图了~

瞄一眼LongAdder(jdk11)的更多相关文章

  1. 瞄一眼CopyOnWriteArrayList(jdk11)

    CopyOnWriteArrayList是ArrayList线程安全的变体.使用写时复制策略进行修改操作. 与之前版本较明显的区别是,jdk11中用来保护所有设值方法(mutator)的Reentra ...

  2. 瞄一眼,带你走进SparkSQL的世界

    本文由  网易云发布. 作者:范欣欣(本篇文章仅限知乎内部分享,如需转载,请取得作者同意授权.) 最近想来,大数据相关技术与传统型数据库技术很多都是相互融合.互相借鉴的.传统型数据库强势在于其久经考验 ...

  3. Java并发编程实战笔记

    如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误.有三种方式可以修复这个问题: i.不在线程之间共享该状态变量 ii.将状态变量修改为不可变的变量 iii.在访问状态变 ...

  4. 后HTML5时代

    十二年前,无论多么复杂的布局,在我们神奇的table面前,都不是问题:十年前,阿捷的一本<网站重构>,为我们开启了新的篇章:八年前,我们研究yahoo.com,惊叹它在IE5下都表现得如此 ...

  5. [深入JUnit] 测试运行的入口

    阅读前提 了解JUnit 对JUnit的内部实现有兴趣 不妨看看[深入JUnit] @Before, @After, @Test的秘密] 代码版本: junit 4.12代码搜索工具: http:// ...

  6. Intellij IDEA 一些不为人知的技巧

    Intellij IDEA 一些不为人知的技巧 2016/12/06 | 分类: 基础技术 | 0 条评论 | 标签: IntelliJ 分享到:38 原文出处: khotyn 今天又听了 Jetbr ...

  7. jq绑定事件的4种方式

    jQuery提供了多种绑定事件的方式,每种方式各有其特点,明白了它们之间的异同点,有助于我们在写代码的时候进行正确的选择,从而写出优雅而容易维护的代码.下面我们来看下jQuery中绑定事件的方式都有哪 ...

  8. [No00007D]2016-面经[上]

    面试常见问题: 题一:"请你自我介绍一下" 思路:1.这是面试的必考题目.2.介绍内容要与个人简历相一致.3.表述方式上尽量口语化.4.要切中要害,不谈无关.无用的内容.5.条理要 ...

  9. [No00006D]下载离线版的github for windows【以Github for Windows 3.0.110.为例】

    目录 先上地址后讲原理: 原理: 11个目录的文件怎么一口气下载呢? 最后,把下好的文件批量名,同时将GitHub.exe.manifest也放到软件根目录下(与GitHub.exe同级): 今后的猜 ...

随机推荐

  1. bootstrap历练实例:面板脚注

    面板脚注 我们可以在面板中添加脚注,只需要把按钮或者副文本放在带有 class .panel-footer 的 <div> 中即可.下面的实例演示了这点: <!DOCTYPE htm ...

  2. HTML5中Web存储

    HTML5 中web存储是一个比cookies更好的一个本地存储方式. 那么什么是HTML5存储呢? 使用HTML5可以在本地存储用户浏览的数据,HTML5技术没有出来之前是使用cookies进行本地 ...

  3. 【细节题 离线 树状数组】luoguP4919 Marisa采蘑菇

    歧义差评:但是和题意理解一样了之后细节依然处理了很久,说明还是水平不够…… 题目描述 Marisa来到了森林之中,看到了一排nn个五颜六色的蘑菇,编号从1-n1−n,这些蘑菇的颜色分别为col[1], ...

  4. CPL学习笔记(二)

    数组 数组(array) 声明通用格式为: typeName arrayName[arraySize]. arrayName指定元素数目,必须为整型常量或const值.(不能是变量) int a[3] ...

  5. NodeJS基础-Buffer

    Buffer用于处理二进制数据流 实例类似于整数数组,大小固定 C++代码在V8堆外分配物理内存 // 创建一个长度为10,且用0填充的Buffer const buf1 = Buffer.alloc ...

  6. 【Kafka】搭建和测试等问题

    1.安装启动kafka #跳转到下载目录cd /opt/setup # 下载安装包 wget http://mirror.bit.edu.cn/apache/kafka/0.10.2.0/kafka_ ...

  7. 如何用纯 CSS 创作一个方块旋转动画

    效果预览 在线演示 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/gjgyWm 可交互视频 ...

  8. 文件处理seek以及修改内容的两种方式

    f.seek(offset,whence)offset代表文件的指针的偏移量,单位是字节byteswhence代表参考物,有三个取值# 0:参照文件的开头# 1:参照当前文件指针所在位置# 2: 参照 ...

  9. 前端之bootstrap

    一.响应式介绍 众所周知,电脑.平板.手机的屏幕是差距很大的,假如在电脑上写好了一个页面,在电脑上看起来不错,但是如果放到手机上的话,那可能就会乱的一塌糊涂,这时候怎么解决呢?以前,可以再专门为手机定 ...

  10. Linux学习-什么是 X Window System

    Unix Like 操作系统不是只能进行服务器的架设而已,在美编.排版.制图.多媒体应用上也是有其 需要的. 这些需求都需要用到图形接口 (Graphical User Interface, GUI) ...