LongAdder

LongAdder.png

Cell

// 避免伪共享 jdk1.8后加入的
@sun.misc.Contended static final class Cell {
    volatile long value;
    Cell(long x) { value = x; }
    // CAS操作,设置value
    final boolean cas(long cmp, long val) {
        return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
    }

    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long valueOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> ak = Cell.class;
            valueOffset = UNSAFE.objectFieldOffset
                (ak.getDeclaredField("value"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

sum

public long sum() {
    Cell[] as = cells; Cell a;
    long sum = base;
    if (as != null) {
        for (int i = 0; i < as.length; ++i) {
            if ((a = as[i]) != null)
                // base + N * cell.value
                sum += a.value;
        }
    }
    return sum;
}

reset

// reset base & cell.value
public void reset() {
    Cell[] as = cells; Cell a;
    base = 0L;
    if (as != null) {
        for (int i = 0; i < as.length; ++i) {
            if ((a = as[i]) != null)
                a.value = 0L;
        }
    }
}

add

public void add(long x) {
    Cell[] as; long b, v; int m; Cell a;
    // 如果有cell的部分,说明要累加到cell
    // 如果cell为空,那么累加base看看
    if ((as = cells) != null || !casBase(b = base, b + x)) {
        boolean uncontended = true;
        // 如果cells还没有初始化,直接进入if
        // 或cells的长度为0
        // 或当前线程的cell位置为空,没有被累加过
        // 或者cell位置不为空,且累加失败,有竞争
        // 以上情况,全部进入下面的longAccumulate
        if (as == null || (m = as.length - 1) < 0 ||
            (a = as[getProbe() & m]) == null ||
            !(uncontended = a.cas(v = a.value, v + x)))
            longAccumulate(x, null, uncontended);
    }
}

longAccumulate

final void longAccumulate(long x, LongBinaryOperator fn,
                          boolean wasUncontended) {
    int h;
    // 看下ThreadLocalRandom是否初始化
    if ((h = getProbe()) == 0) {
        ThreadLocalRandom.current(); // force initialization
        h = getProbe();
        wasUncontended = true;
    }
    boolean collide = false;                // True if last slot nonempty
    // 自旋
    for (;;) {
        Cell[] as; Cell a; int n; long v;
        // 如果cells有东西
        if ((as = cells) != null && (n = as.length) > 0) {
            // 如果线程对应的坑位为空
            if ((a = as[(n - 1) & h]) == null) {
                // cellsbusy为0,代表现在cells稳定,那么可以开始针对当前线程增加坑位
                // 换句话说,如果发现cellsbusy为1,坑位就没机会增加了。
                if (cellsBusy == 0) {       // Try to attach new Cell
                    Cell r = new Cell(x);   // Optimistically create
                    // 到这里,准备正式开始,第一时间先锁定cells,也就是先设置cellsbusy为1
                    if (cellsBusy == 0 && casCellsBusy()) {
                        boolean created = false;
                        try {               // Recheck under lock
                            Cell[] rs; int m, j;
                            // 再次定位到坑位
                            if ((rs = cells) != null &&
                                (m = rs.length) > 0 &&
                                rs[j = (m - 1) & h] == null) {
                                // 将cell填充进去,不要忘记这个坑位记录了你要put的value
                                rs[j] = r;
                                created = true;
                            }
                        } finally {
                            // 最终释放cellsbusy,也就是解锁cells
                            cellsBusy = 0;
                        }
                        // 如果已经成功创建坑位,那么退出自旋,否则继续自旋
                        if (created)
                            break;
                        continue;           // Slot is now non-empty
                    }
                }
                // 到这里说明cells正在被其他线程锁定,记录下有冲突的事实,准备去rehash
                collide = false;
            }
            // 这里说明外部调用处在设置线程对应的坑位时失败,有竞争线程
            // wasUncontended的意义在于,如果有竞争,说明线程的probe冲突,准备去rehash
            // 对应下面的advanceProbe,这样再次自旋后,会去关注新的坑位,也就没有冲突一说了
            else if (!wasUncontended)       // CAS already known to fail
                wasUncontended = true;      // Continue after rehash
            // 到这里,说明该线程的坑位已经有了,直接累加就好,如果成功当然最好了
            else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                         fn.applyAsLong(v, x))))
                break;
            // 如果上面累加失败,看下cells是否已经到了上限或cells已经变更了,比如扩容了。
            // 这里也记录下有冲突,准备去rehash
            else if (n >= NCPU || cells != as)
                collide = false;            // At max size or stale
            // 设置冲突标识,准备去rehash
            else if (!collide)
                collide = true;
            // 到这里说明,就算有了cells,但是该位置上累加失败,数组还可以扩容
            // 既然你不让我加,竞争这么厉害,那么扩容试试看
            // 当然了,先锁定cells
            else if (cellsBusy == 0 && casCellsBusy()) {
                try {
                    // 如果cells没变化
                    if (cells == as) {      // Expand table unless stale
                        // 容量扩大一倍
                        Cell[] rs = new Cell[n << 1];
                        将旧的转移到新的cells里面
                        for (int i = 0; i < n; ++i)
                            rs[i] = as[i];
                        cells = rs;
                    }
                } finally {
                    // 解锁cells
                    cellsBusy = 0;
                }
                collide = false;
                // 扩容完,再自旋看看,能否成功
                continue;                   // Retry with expanded table
            }
            // 重新计算新的probe值以对应到不同的下标元素,然后重试。  
            h = advanceProbe(h);
        }
        // cells还未初始化或为空,先锁定cells
        else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
            boolean init = false;
            try {                           // Initialize table
                if (cells == as) {
                    // cells的大小必须是2的幂,好length-1&h进行求余
                    Cell[] rs = new Cell[2];
                    // 设置h对应的下标位置
                    rs[h & 1] = new Cell(x);
                    cells = rs;
                    init = true;
                }
            } finally {
                // 解锁
                cellsBusy = 0;
            }
            // 如果初始化完毕,退出自旋
            if (init)
                break;
        }
        // 说明cells还没有初始化,但是cells被别人锁定了
        // 那么尝试着加到base看看,如果成功,那么退出自旋
        else if (casBase(v = base, ((fn == null) ? v + x :
                                    fn.applyAsLong(v, x))))
            break;                          // Fall back on using base
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容

  • LongAdder 源码解读 源码解读部分按照我得理解翻译和解读注解并添加相关的部分代码解读 保持一个或者多个变量...
    DeanChangDM阅读 438评论 0 3
  • 浅析LongAdder 前言 上文中分析了AtomicLong以及Unsafe,本文将为大家带来LongAdder...
    LNAmp阅读 13,699评论 4 14
  • 一、简介  在之前的《ConcurrentHashMap深入剖析(JDK8)》文章中,我们看到了CounterCe...
    SunnyMore阅读 1,830评论 1 6
  • 奔驰女车主事件经过多日多家媒体报道,尤其是央视新闻今日说法的跟踪报道,不停发酵,今日,暂告结束,因为据报道,奔驰女...
    边境baby阅读 319评论 4 1
  • 有时候总是莫名其妙的乱想,无助中疲惫无力。乱想着慢慢陷入恐慌,大脑已经不能控制,像行走的丧尸,无关痛痒。生死在某一...
    麓枷王阅读 158评论 0 2