LongAdder升级版的AtomicLong

LongAdder

在使用AtomicLong时,如果是在高并发场景下去同时竞争修改同一个原子变量,由于内部使用的是CAS,只会有一个线程修改成功,这就造成了大量的线程竞争失败后,通过无限循环来不断的进行CAS操作,白白的浪费了CPU资源,在JDK8中为了解决这种问题,提供了LongAdder来进行原子性递增递减

当多线程争夺同一个Cell原子变量时如果失败,并不是在当前Cell变量上一直自旋CAS重试,而是会尝试在其他Cell变量上进行CAS尝试,增加了CAS成功的可能性

最终,获取LongAdder的当前值时,会把所有Cell变量的value值累加后再加上base值

java 复制代码
// 真实值为base值与cells数组中所有Cell元素中value值的累加
// 在LongAdder中维护了一个Cell数组,分担对单个变量进行竞争的开销
transient volatile Cell[] cells;
// 基值变量
transient volatile long base;
// 用来实现自旋锁,状态值只有0和1,当创建Cell元素,扩容Cell数组和初始化Cell数组时,使用CAS操作该变量来保证同时只有一个线程可以进行其中一个Cell的操作
transient volatile int cellsBusy;

@sun.misc.Contended static final class Cell {
  			// 每个Cell中有一个初始值为0的long类型变量value
        volatile long value;
        Cell(long x) { value = x; }
        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);
            }
        }
    }


// 进行加一减一操作
public void add(long x) {
  Cell[] as; long b, v; int m; Cell a;
  // 如果cells为null的话在base的基础上进行累加
  if ((as = cells) != null || !casBase(b = base, b + x)) {
    boolean uncontended = true;
    // 选择其中一个Cell进行操作
    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);
  }
}


final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
  int h;
  // 初始化当前线程的变量h,用于计算当前线程应该被分到cells数组中的哪个元素
  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如果为null,说明还没有初始化
    // 不为null,说明已经初始化过了
    if ((as = cells) != null && (n = as.length) > 0) {
      // 数组cells已经扩容,但是有的Cell还没有填充,此时为null,需要新增一个Cell元素到cells数组中
      if ((a = as[(n - 1) & h]) == null) {
        if (cellsBusy == 0) {       // Try to attach new Cell
          Cell r = new Cell(x);   // Optimistically create
          // 添加新元素需要将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) {
                rs[j] = r;
                created = true;
              }
            } finally {
              cellsBusy = 0;
            }
            if (created)
              break;
            continue;           // Slot is now non-empty
          }
        }
        collide = false;
      }
      else if (!wasUncontended)       // CAS already known to fail
        wasUncontended = true;      // Continue after rehash
      // 当前Cell存在,则执行CAS操作
      else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                   fn.applyAsLong(v, x))))
        break;
      // 当前Cell数组元素个数大于CPU个数
      else if (n >= NCPU || cells != as)
        collide = false;            // At max size or stale
      // 是否有冲突(多个线程访问了cells数组中的同一个Cell)
      else if (!collide)
        collide = true;
      // 只有当前元素个数没有达到CPU个数,并且有冲突,会进行扩容
      // 进行扩容时casCellsBusy方法来将cellsBusy设置为1
      else if (cellsBusy == 0 && casCellsBusy()) {
        try {
          
          if (cells == as) {      // Expand table unless stale
            // 扩容为原来的2倍
            Cell[] rs = new Cell[n << 1];
            // 复制原数组中的Cell元素到新的cells数组中
            for (int i = 0; i < n; ++i)
              rs[i] = as[i];
            cells = rs;
          }
        } finally {
          cellsBusy = 0;
        }
        collide = false;
        continue;                   // Retry with expanded table
      }
      // 重新计算h,找到空闲的Cell
      h = advanceProbe(h);
    }
    // 初始化Cell数组,cellsBusy为0表示当前cells数组没有在被初始化或者扩容,也没有在新建Cell元素;为1表示当前cells数组在被初始化或者扩容,或者在新建Cell元素
    // casCellsBusy来切换cellsBusy的状态
    else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
      boolean init = false;
      try {                           // Initialize table
        if (cells == as) {
          // 初始化的个数为2个
          Cell[] rs = new Cell[2];
          // 使用h&1来计算当前线程应该访问哪个Cell
          rs[h & 1] = new Cell(x);
          cells = rs;
          init = true;
        }
      } finally {
        cellsBusy = 0;
      }
      if (init)
        break;
    }
    else if (casBase(v = base, ((fn == null) ? v + x :
                                fn.applyAsLong(v, x))))
      break;                          // Fall back on using base
  }
}

zhhll.icu/2021/多线程/基础...

本文由mdnice多平台发布

相关推荐
李慕婉学姐4 小时前
【开题答辩过程】以《基于JAVA的校园即时配送系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言·数据库
奋进的芋圆5 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin6 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model20056 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉6 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国6 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_941882486 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈7 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_997 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹7 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理