LongAdder和AtomicLong源码分析

本文深入解析了LongAdder和AtomicLong两种线程安全的计数器类的使用场景与内部实现机制。LongAdder更适用于高并发环境下频繁进行加减操作,而AtomicLong适合于频繁查询的场景。通过源码分析,详细解释了LongAdder如何通过cells数组和base字段高效处理并发写操作。

概述

介绍

AtomicLongLongAdder
使用do-while循环进行CAS操作直到数据操作成功将变更的大小存储在base中,如果存在竞争,每个线程的变更都存储在所属线程的cell中。

CAS

样例代码

  public final native boolean compareAndSetLong(Object o, long offset,
                                                  long expected,
                                                  long x);

说明

参数作用
Object o当前对象的引用地址(可以看成起始地址)
long offset当前对象变更字段相对于对象起始地址的偏移量
long expected期望的值
long x如果期望的值和该字段在内存中的值相同,那么就会将x进行更新

LongAdder结构

LongAdder与AtomicLong的对比

LongAdder相比于AtomicLong更适合高并发情况下,频繁进行加减变更而较少查询的操作。

AtomicLong相比于LongAdder更适合高并发情况下,频繁进行查询而较少进行加减变更的操作。

AtomicLong的精度比LongAdder的精度更高。

源码分析

add(long x)方法

/**
     * Adds the given value.
     *
     * @param x the value to add
     */
    public void add(long x) {
        Cell[] cs; long b, v; int m; Cell c;
        if ((cs = cells) != null || !casBase(b = base, b + x)) {
            boolean uncontended = true;
            if (cs == null || (m = cs.length - 1) < 0 ||
                (c = cs[getProbe() & m]) == null ||
                !(uncontended = c.cas(v = c.value, v + x)))
                longAccumulate(x, null, uncontended);
        }
    }

执行到longAccumulate(x, null, uncontended)的几种情况

  1. 第一个if

    1. 情况1.1(cs = cells) != null:cells不为null(已经存在竞争)
    2. 情况1.2!casBase(b = base, b + x):cells为null,但是对base进行赋值的时候失败说明存在竞争。
  2. 第二个if

    1. 当情况为1.1的时候(c = cs[getProbe() & m]) == null ||!(uncontended = c.cas(v = c.value, v + x)):cells不为null

      c = cs[getProbe() & m]) == null:如果线程对应的cells的槽位是null,说明之前未设置过值,uncontended为true。

      !(uncontended = c.cas(v = c.value, v + x):如果线程对应的cells的槽位不是null,说明之前已经设置过值,然后使用CAS进行设置新的值。如果设置成功则不会向下执行,如果设置失败说明存在竞争uncontended为false。

    2. 当情况为1.2的时候cs == null || (m = cs.length - 1) < 0:cells为null

      直接执行longAccumulate(x, null, uncontended),uncontended为true。

longAccumulate方法

//|--进入到该函数的几种情况
//	|--1. CASE1: cells==null,并且在设置base的时候存在多个线程的竞争。需要对cells进行初始化。
//	|--2.	cells已经进行初始化
//		|--2.1 CASE2:线程对应的cells的槽位为null。对该槽位进行赋值 new Cell(x)。
//		|--2.2 CASE3:在对线程所对应的槽位进行赋值的时候,存在多个线程的竞争。可能会发生扩容和重试。
final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
  /**
   *变量说明:
   *h:线程的hash值
   *wasUncontended cell槽位是否存在竞争:false 存在竞争,true 不存在竞争
   *collide 是否需要扩容或初始化:false 不需扩容,true 需要扩容
   *cellsBusy 同步标志(Lock锁就使用到了同步标志state):0 可以获取,1 不可获取
  **/
        int h;
  			//在未使用ThreadLocalRandom.current()计算线程的hash之前,默认都是0。
         //0&(length-1)=0,所以最开始存在的cell竞争是0槽位。此时需要调用ThreadLocalRandom.current()去计算线程的hash。
         //该位置的状态对于此线程未知,假设不存在竞争wasUncontended = true。如果已经进行过hash初始化,则不会进入if。
        if ((h = getProbe()) == 0) {
            ThreadLocalRandom.current(); // force initialization
            h = getProbe();
            wasUncontended = true;
        }
        boolean collide = false;                // True if last slot nonempty
  
        done: for (;;) {
            Cell[] cs; Cell c; int n; long v;
          //<1> 对应CASE2、3的情况
            if ((cs = cells) != null && (n = cs.length) > 0) {//<1>
              //<1> 1). 对应CASE2的情况
                if ((c = cs[(n - 1) & h]) == null) {
                  //1.1). 这里有些类似于实现单例模式的 双重检查,创建cell,并将其设置到线程所属的槽位(null)中。在设置之前会获取同步标志,设置成功之后释放同步标志。
                  //	因为并发,如果在这个过程中其他线程已经对该null的槽位进行设置(哈希碰撞),则会转换成CASE3,注意:wasUncontended仍然是true。
                    if (cellsBusy == 0) {       // Try to attach new Cell
                        Cell r = new Cell(x);   // Optimistically create
                        if (cellsBusy == 0 && casCellsBusy()) {
                            try {               // Recheck under lock
                                Cell[] rs; int m, j;
                                if ((rs = cells) != null &&
                                    (m = rs.length) > 0 &&
                                    rs[j = (m - 1) & h] == null) {
                                    rs[j] = r;
                                    break done;
                                }
                            } finally {
                                cellsBusy = 0;
                            }
                            continue;           // Slot is now non-empty
                        }
                    }
                  //如果未获取成功,说明有线程可能在进行扩容和初始化,所以将collide赋值false。
                    collide = false;
                }
              //<1> 1.2). CASE3的处理使用 h = advanceProbe(h)进行rehash,同自旋之前的原因一样,将wasUncontended设置为true。
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
              //<1> 1.3). CASE2的null槽位被其他线程设置,或是rehash过。会尝试对cell进行一次CAS赋值,成功则结束方法。
                else if (c.cas(v = c.value,
                               (fn == null) ? v + x : fn.applyAsLong(v, x)))
                    break;
              //cells数组的长度不能超过电脑的处理器数量。因为同时只能由处理器数量的线程被执行,如果长度超过会浪费空间,并设置为不可扩容。
                else if (n >= NCPU || cells != cs)
                    collide = false;            // At max size or stale
                else if (!collide)
                    collide = true;
              //<1> 1.4). 进行扩容,容量为原来的2倍。会获取同步标志。
              //扩容的条件:线程的槽位不为null,并且存在竞争 wasUncontended = true,并且在尝试对cell进行CAS失败后才会进行扩容。
                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;                   // Retry with expanded table
                }
                h = advanceProbe(h);//rehash
            }
          //<2> 对 CASE1 的处理,获取同步标志并进行cells的初始化。初始容量为2.
            else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
                try {                           // Initialize table
                    if (cells == cs) {
                        Cell[] rs = new Cell[2];
                        rs[h & 1] = new Cell(x);
                        cells = rs;
                        break done;
                    }
                } finally {
                    cellsBusy = 0;
                }
            }
            // Fall back on using base
          //<3> 对应 CASE1 的一种处理,若cells为null,并且有线程在进行扩容或初始化,则会尝试使用CAS将新值付给base。
            else if (casBase(v = base,
                             (fn == null) ? v + x : fn.applyAsLong(v, x)))
                break done;
        }
    }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值