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. BCB:UTF8Encode、AnsiToUtf8

    UTF8Encode: Call Utf8Encode to convert a Unicode string to UTF-8. WS is the Unicode string to conver ...

  2. CodePlus #4 最短路

    题目传送门 北极为什么会有企鹅啊,而且北纬91°在哪啊? 关键在建图 因为任意两个城市间都可以互相到达,再加上还有"快捷通道",光是建图就已经\(\rm{T}\)了-- 但这题给了 ...

  3. Nginx: ubuntu系统上如何判断是否安装了Nginx?

    问题描述:ubuntu系统上,如何查看是否安装了Nginx? 解决方法:输入命令行:ps -ef | grep nginx master process后面就是Nginx的安装目录. 延伸:1. 如何 ...

  4. PHP 存储密码

    最佳实践是 $hashedPassword = password_hash('my super cool password', PASSWORD_DEFAULT); $res = password_v ...

  5. C++ 学习笔记 开篇

    从大一开始学习C语言,大学期间做了许多嵌入式的开发项目,毕业后从事嵌入式开发工作主要的开发语言也是C语言.虽然期间断断续续的学习过C++,做过QT.C#上位机但也只是在其他语言的外壳下使用C在开发,始 ...

  6. 初涉网络流[EK&dinic]

    主要还是板子 Edmonds-Karp 从S开始bfs,直到找到一条到达T的路径后将该路径增广,并重复这一过程. 在处理过程中,为了应对“找到的一条路径把其他路径堵塞”的情况,采用了建反向弧的方式来实 ...

  7. ERROR 1045 (28000): Access denied for user 'xxx'@'localhost' (using password: YES) MYSQL 新建用户 无法登录 问题解决方法

    使用mysql ,出现新建账户无法登录问题 查看 user列表中,有部分账户没有设置密码,将全部重新设置一遍密码,然后还是无法登录. 使用命令 update user set password=pas ...

  8. PHP redis使用命令

    很有用;以下是redis官方提供的命令使用技巧: 下载地址如下: https://github.com/owlient/phpredis(支持redis 2.0.4) Redis::__constru ...

  9. Lecture 2

    1. Coordinate(坐标) data for GIS real coordinate system:Cartesian coordinate systems(笛卡尔坐标系) from 3D t ...

  10. GoF23种设计模式之行为型模式之命令模式

    一.概述 将一个请求封装为一个对象,从而可以使用不同的请求对客户端进行参数化.对请求排队或记录请求日志,以及支持撤销的操作. 二.适用性 1.当抽象出待执行的动作以参数化某个对象的时候. 2.当需要在 ...