看图学源码之 Atomic 类源码浅析二(cas + 分治思想的原子累加器)

原子累加器

相较于上一节看图学源码 之 Atomic 类源码浅析一(cas + 自旋操作的 AtomicXXX原子类)说的的原子类,原子累加器的效率会更高

image-20231205182958814

XXXXAdderXXXAccumulator 区别就是 Adder只有add 方法,Accumulator是可以进行自定义运算方法的

始于 Striped64
abstract class Striped64 extends Number {

  // cpu 运行核数, 控制数组的大小
   static final int NCPU = Runtime.getRuntime().availableProcessors();
  
  // 当非空时,大小是 2 的幂。 
  transient volatile Cell[] cells;
  
  // 表初始化竞争期间的后备值  通过  CAS 更新  就是 value
  transient volatile long base;
  
  // 锁的 标志位 调整单元大小和/或创建单元时使用自旋锁(通过 CAS 锁定)
  transient volatile int cellsBusy;
  
  
    //Base的cas 操作
    final boolean casBase(long cmp, long val) {
        return UNSAFE.compareAndSwapLong(this, BASE, cmp, val);
    }

    // CellsBusy的cas操作
    final boolean casCellsBusy() {
        return UNSAFE.compareAndSwapInt(this, CELLSBUSY, 0, 1);
    }

  
// 主要是给不同的线程找到数组中不同的下标
    // 当前线程的探测值
    static final int getProbe() {
        return UNSAFE.getInt(Thread.currentThread(), PROBE);
    }
    // 给定线程的给定探测值
    static final int advanceProbe(int probe) {
        probe ^= probe << 13;   // xorshift
        probe ^= probe >>> 17;
        probe ^= probe << 5;
        UNSAFE.putInt(Thread.currentThread(), PROBE, probe);
        return probe;
    }
  ...
  
  ...
    // Unsafe mechanics
    // Unsafe 的获取 和 偏移量的获取
    private static final sun.misc.Unsafe UNSAFE;
    private static final long BASE;
    private static final long CELLSBUSY;
    private static final long PROBE;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> sk = Striped64.class;
            BASE = UNSAFE.objectFieldOffset(sk.getDeclaredField("base"));
            CELLSBUSY = UNSAFE.objectFieldOffset(sk.getDeclaredField("cellsBusy"));
            Class<?> tk = Thread.class;
            PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

}
内部类

@sun.misc.Contended——解决伪共享,进行字节填充

@sun.misc.Contended static final class Cell {
  // 操作的数
    volatile long value;
  
  // 构造器
    Cell(long x) { value = x; }
  
  // 进行 cas 
    final boolean cas(long cmp, long val) {
        return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
    }
}
LongAdder

可能会出现一边正在进行 累加操作,一边又在执行求和操作,所以就导致了不是 强一致性,而是最终一致性

image-20231207001047541

image-20231207001116782

image-20231207001130302

image-20231207001138259

public class LongAdder extends Striped64 implements Serializable {
    private static final long serialVersionUID = 7249069246863182397L;

    public LongAdder() {
    }


    public void add(long x) {
        Cell[] as; long b, v; int m; Cell a;
      // 数组是不是 null(判断有没有发生竞争,因为只有竞争发生才会初始化数组)
      // 没有初始化(就是没有竞争) 直接对 base 的值+x 失败
        if ((as = cells) != null || !casBase(b = base, b + x)) {
          //有竞争的时候
            boolean uncontended = true;
          // 数组还是没有初始化 || 数组初始化,但是数组的长度  < 0 || 数组中的该位置的值是 null (表示这个下标没有初始化)|| cas的方式把当前位置的值 + x ,cas 失败
            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);
        }
    }

  
    public void increment() {
        add(1L);
    }

    public void decrement() {
        add(-1L);
    }

  // 返回当前总和。返回的值不是原子快照;
  // 在没有并发更新的情况下调用会返回准确的结果,但计算总和时发生的并发更新可能不会被合并
	// 所以不是 强一致性的
    public long sum() {
        Cell[] as = cells; Cell a;
        long sum = base;
      //数组不是 null
        if (as != null) {
          //遍历数组,
            for (int i = 0; i < as.length; ++i) {
              //数组中的槽位不是 null,对槽位的数据进行运算,赋值加到base中
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
      //返回总的值
        return sum;
    }

 // 将保持总和为零的变量重置。此方法可能是创建新加法器的有用替代方法,但仅在没有并发更新时才有效。
 // 由于此方法本质上是活泼的,因此仅应在  已知没有线程同时更新时才使用它。
    public void reset() {
        Cell[] as = cells; Cell a;
        base = 0L;
        if (as != null) {
            // 数组存在,遍历数组,将数组中所有的值设置为 0 
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    a.value = 0L;
            }
        }
    }

    // 相当于sum后跟reset 
    // 该方法可以应用于例如 多线程计算之间的静止点期间。
    // 如果此方法同时有更新,则不能保证返回值是重置之前发生的最终值。
    public long sumThenReset() {
        Cell[] as = cells; Cell a;
        long sum = base;
        base = 0L;
        if (as != null) {
          // 数组存在,遍历数组,先求和  后把数组中所有的值设置为 0 
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null) {
                    sum += a.value;
                    a.value = 0L;
                }
            }
        }
        return sum;
    }


  // 返回sum的字符串表示形式
    public String toString() {
        return Long.toString(sum());
    }


   // 返回sum
    public long longValue() {
        return sum();
    }

  //缩小基元转换后以 int 形式返回sum 
    public int intValue() {
        return (int)sum();
    }

  // 加宽基元转换后以float形式返回sum 
    public float floatValue() {
        return (float)sum();
    }
  //加宽基元转换后以 double 形式返回sum 
    public double doubleValue() {
        return (double)sum();
    }

    private static class SerializationProxy implements Serializable {
        private static final long serialVersionUID = 7249069246863182397L;

        private final long value;

      // sum() 返回的当前值。
        SerializationProxy(LongAdder a) {
            value = a.sum();
        }
				// 返回一个LongAdder对象,其初始状态由该代理保存。
        private Object readResolve() {
            LongAdder a = new LongAdder();
            a.base = value;
            return a;
        }
    }

    private Object writeReplace() {
        return new SerializationProxy(this);
    }
  
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.InvalidObjectException {
        throw new java.io.InvalidObjectException("Proxy required");
    }

}
striped64中的 longAccumulate

final void longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended) {
    int h;
   // 拿 hash 值,拿不到强制获取
    if ((h = getProbe()) == 0) {
        ThreadLocalRandom.current(); // force initialization
        h = getProbe();
      // 将 wasUncontended 的值设为 true,表示当前线程是未争用的。
        wasUncontended = true;
    }
    boolean collide = false;                // True if last slot nonempty
    for (;;) {
        Cell[] as; Cell a; int n; long v;
   // 分支 1
      //数组已经初始化,出现了竞争
        if ((as = cells) != null && (n = as.length) > 0) {
        // 分支1.1
          		// 当前位置的值是null
            if ((a = as[(n - 1) & h]) == null) {
              // 锁的标志位 == 0 ,没有加锁 
                if (cellsBusy == 0) {       // Try to attach new Cell
                    Cell r = new Cell(x);   // Optimistically create
                   // 加锁
                    if (cellsBusy == 0 && casCellsBusy()) {
                        boolean created = false;   
                        try {               // Recheck under lock
                            Cell[] rs; int m, j;
                          //  加锁之后再次检查指定位置是否为空 
                          //  数组初始化过了 && 当前位置的值不是null
                            if ((rs = cells) != null &&
                                (m = rs.length) > 0 &&
                                rs[j = (m - 1) & h] == null) {
                              // 给数组的指定位置设置为  之前设置过的cell对象
                                rs[j] = r;
                              // 创建成功
                                created = true;
                            }
                        } finally {
                          // 解锁
                            cellsBusy = 0;
                        }
                        if (created)
                            break;
                        continue;           // Slot is now non-empty
                    }
                }
              // 有人加锁了,发生了冲突 避免在当前位置发生碰撞的情况下继续进行操作,将 collide 标志位设置为 false。
                collide = false;
            }
          // 分支1.2
          	 // 没有发生竞争
            else if (!wasUncontended)       // CAS already known to fail
              // 此时是发生了竞争
                wasUncontended = true;      // Continue after rehash
          // 分支1.3
          		// cas 的方式更新 此位置的值, cas 失败表示有线程正在此位置执行操作
            else if (a.cas(v = a.value, ((fn == null) ? v + x :fn.applyAsLong(v, x))))
                break;
          // 分支1.4
              // n > cpu 的个数  当前分段数组的长度是否已经达到或超过了处理器的数量。
              //如果是,说明分段数组已经达到了最大的容量或者已经很大了,不再需要继续进行扩容操作。
           		
              // 或者 cells  发生了变化,当前线程获取到的分段数组引用是否与共享变量中的引用相等。
              // 如果不相等,说明在当前线程获取到分段数组的过程中,有其他线程进行了修改,即分段数组已经发生了变化。
            else if (n >= NCPU || cells != as)
                collide = false;            // At max size or stale
          // 分支1.5
           // 此时是发生了碰撞的 collide 被设置为 true 
            else if (!collide)
                collide = true;
          // 分支1.6 
           			// 扩容
          							// 没有被锁 && cas  的方式 成功加锁 
            else if (cellsBusy == 0 && casCellsBusy()) {
                try {
                  			// 数组没有变化
                    if (cells == as) {      // Expand table unless stale
                       // as 数组长度扩大一倍
                        Cell[] rs = new Cell[n << 1];
                     	// 元素直接赋值
                        for (int i = 0; i < n; ++i)
                            rs[i] = as[i];
                        cells = rs;
                    }
                } finally {
                   // 解锁
                    cellsBusy = 0;
                }
              //没有冲突
                collide = false;
              //扩容成功,继续循环
                continue;                   // Retry with expanded table
            }
           // 更新 hash 值
            h = advanceProbe(h);
        }
// 分支 2
      // 此处数组没有进行初始化,此时进行初始化
      // 锁的标志为 == 0  &&  数组没有改变(多线程情况下该线程没有被其他线程初始化)  && cas 成功的把锁的标志位 设置为 1(加锁流程)
      // 当前的 cells 数组没有被其他线程占用,并且成功获取了 cellsBusy 锁
        else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
            boolean init = false;
            try {                           // Initialize table
              // 加完锁之后再次判断一次  cells 数组没有发生过变化
                if (cells == as) { 
                  // 数组 长度默认为2
                    Cell[] rs = new Cell[2];
                  // 给rs 赋值为 要加入的 x
                    rs[h & 1] = new Cell(x);
                  // 将 cells 数组变更为 rs
                    cells = rs;
                  // 初始化成功
                    init = true;
                }
            } finally {
              // 解锁
                cellsBusy = 0;
            }
            if (init)    //初始化成功
                break;    // 退出循环
        }
// 分支 3  cas 的方式 操作 base  , fn 函数式接口的方法  == null 默认加法,否则就是定义的方法
        else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x))))
            break;                          // Fall back on using base  退出循环
    }
}
LongAccumulator

image-20231207001717871

image-20231207001732877

在这里插入图片描述

image-20231207001843693

public class LongAccumulator extends Striped64 implements Serializable {
    private static final long serialVersionUID = 7249069246863182397L;

    private final LongBinaryOperator function;
    private final long identity;

    public LongAccumulator(LongBinaryOperator accumulatorFunction,
                           long identity) {
        this.function = accumulatorFunction;
        base = this.identity = identity;
    }

    // 更新值
    public void accumulate(long x) {
        Cell[] as; long b, v, r; int m; Cell a;
      // 有竞争  || ( cas 运算 base 的值成功  && 对 base进行cas更新失败 )
        if ((as = cells) != null ||
            //  function.applyAsLong  函数式接口 
            (r = function.applyAsLong(b = base, x)) != b && !casBase(b, r)) {
            boolean uncontended = true;
					// 出现了竞争
          // 数组还是没有初始化 || 数组初始化,但数组的长度 < 0 || 数组中的该位置的值是 null (表示这个下标没有初始化)|| (cas的方式运算当前位置的值 失败 && cas 更新当前位置的值也失败)
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended =
                  (r = function.applyAsLong(v = a.value, x)) == v ||
                  a.cas(v, r)))
                longAccumulate(x, function, uncontended);
        }
    }

    // 返回当前值。返回的值不是原子快照;
    // 在没有并发更新的情况下调用会返回准确的结果,但计算值时发生的并发更新可能不会被合并。
     
    public long get() {
        Cell[] as = cells; Cell a;
        long result = base;
       // 数组存在
        if (as != null) {
          // 遍历数组
            for (int i = 0; i < as.length; ++i) {
              //数组中的槽位不是 null
                if ((a = as[i]) != null)
                  //对槽位的数据进行运算,赋值加到base中
                    result = function.applyAsLong(result, a.value);
            }
        }
      // 并返回总的值
        return result;
    }

    // 重置变量以维护对标识值的更新。
  	// 此方法可能是创建新更新程序的有用替代方法,但仅在没有并发更新时才有效。
    // 由于此方法本质上是活泼的,因此仅应在已知没有线程同时更新时才使用它。 
    public void reset() {
        Cell[] as = cells; Cell a;
        base = identity;
       // 数组存在
        if (as != null) {
          // 遍历数组
            for (int i = 0; i < as.length; ++i) {
              //数组中的槽位不是 null
                if ((a = as[i]) != null)
                  //将槽位的值设置为  identity
                    a.value = identity;
            }
        }
    }

  // 效果相当于get后面跟着reset 。
  // 该方法可以应用于例如多线程计算之间的静止点期间。
  // 如果此方法同时有更新,则不能保证返回值是重置之前发生的最终值。
    public long getThenReset() {
        Cell[] as = cells; Cell a;
        long result = base;
        base = identity;
      // 数组存在
        if (as != null) {
          // 遍历数组
            for (int i = 0; i < as.length; ++i) {
              //数组中的槽位不是 null
                if ((a = as[i]) != null) {
                  // 将槽位的值设置为  identity
                  // 对槽位的数据进行运算,赋值加到base中
                    long v = a.value;
                    a.value = identity;
                    result = function.applyAsLong(result, v);
                }
            }
        }
        return result;
    }


    public String toString() {
        return Long.toString(get());
    }


    public long longValue() {
        return get();
    }


    public int intValue() {
        return (int)get();
    }


    public float floatValue() {
        return (float)get();
    }


    public double doubleValue() {
        return (double)get();
    }

    /**
     * 序列化代理,用于避免以序列化形式引用非公共 Striped64 超类
     */
    private static class SerializationProxy implements Serializable {
        private static final long serialVersionUID = 7249069246863182397L;

        private final long value;

        private final LongBinaryOperator function;

        private final long identity;

        SerializationProxy(LongAccumulator a) {
            function = a.function;
            identity = a.identity;
            value = a.get();
        }


        private Object readResolve() {
            LongAccumulator a = new LongAccumulator(function, identity);
            a.base = value;
            return a;
        }
    }


    private Object writeReplace() {
        return new SerializationProxy(this);
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.InvalidObjectException {
        throw new java.io.InvalidObjectException("Proxy required");
    }

}

在Double 中会有 doubleToRawLongBits的操作,主要是检查数组越界的

DoubleAdder

image-20231207001419024

public class DoubleAdder extends Striped64 implements Serializable {
    private static final long serialVersionUID = 7249069246863182397L;

    /*
请注意,我们必须使用“long”作为底层表示,因为 double 没有compareAndSet,因为任何 CAS 实现中使用的按位等于与双精度等于不同
然而,我们仅使用 CAS 来检测和缓解争用,无论如何,按位等于效果最好。
原则上,这里使用的 longdouble 转换在大多数平台上基本上应该是免费的,因为它们只是重新解释位。
     */


    public DoubleAdder() {
    }


    public void add(double x) {
        Cell[] as; long b, v; int m; Cell a;
      // 数组存在 || 对 base 进行 cas运算操作失败  
if ((as = cells) != null ||!casBase(b = base,Double.doubleToRawLongBits(Double.longBitsToDouble(b) + x))) {
   // 
            boolean uncontended = true;
  // 数组为 空 || 数组的长度 < 0  || 当前位置的值为 null  ||  对该位置的值进行cas 运算失败
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value,
                                      Double.doubleToRawLongBits
                                      (Double.longBitsToDouble(v) + x))))
                doubleAccumulate(x, null, uncontended);
        }
    }

    /**
    返回当前总和。
    返回的值不是原子快照;在没有并发更新的情况下调用会返回准确的结果,但计算总和时发生的并发更新可能不会被合并。
    由于浮点算术不是严格关联的,因此返回的结果不需要与在单个变量的一系列连续更新中获得的值相同。
     */
    public double sum() {
        Cell[] as = cells; Cell a;
        double sum = Double.longBitsToDouble(base);
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += Double.longBitsToDouble(a.value);
            }
        }
        return sum;
    }

    /**
    将保持总和为零的变量重置。
    此方法可能是创建新加法器的有用替代方法,但仅在没有并发更新时才有效。
    由于此方法本质上是活泼的,因此仅应在已知没有线程同时更新时才使用它。
     */
    public void reset() {
        Cell[] as = cells; Cell a;
        base = 0L; // relies on fact that double 0 must have same rep as long
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    a.value = 0L;
            }
        }
    }

    /**
     相当于sum后跟reset 。
     该方法可以应用于例如多线程计算之间的静止点期间。如果此方法同时有更新,则不能保证返回值是重置之前发生的最终值。
     */
    public double sumThenReset() {
        Cell[] as = cells; Cell a;
        double sum = Double.longBitsToDouble(base);
        base = 0L;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null) {
                    long v = a.value;
                    a.value = 0L;
                    sum += Double.longBitsToDouble(v);
                }
            }
        }
        return sum;
    }

   
    public String toString() {
        return Double.toString(sum());
    }

  
    public double doubleValue() {
        return sum();
    }

   
    public long longValue() {
        return (long)sum();
    }

   
    public int intValue() {
        return (int)sum();
    }

   
    public float floatValue() {
        return (float)sum();
    }

  
    private static class SerializationProxy implements Serializable {
        private static final long serialVersionUID = 7249069246863182397L;

       
        private final double value;

        SerializationProxy(DoubleAdder a) {
            value = a.sum();
        }

        private Object readResolve() {
            DoubleAdder a = new DoubleAdder();
            a.base = Double.doubleToRawLongBits(value);
            return a;
        }
    }

    private Object writeReplace() {
        return new SerializationProxy(this);
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.InvalidObjectException {
        throw new java.io.InvalidObjectException("Proxy required");
    }

}
striped64中的 doubleAccumulate

和上面的 striped64中的 longAccumulate 几乎一模一样,只有doubleToRawLongBits部分的细微差别

final void doubleAccumulate(double x, DoubleBinaryOperator fn,boolean wasUncontended) {
    int h;
     // 拿 hash 值,拿不到强制获取
    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;
// 分支 1
      //数组已经初始化,出现了竞争
        if ((as = cells) != null && (n = as.length) > 0) {
          // 分支1.1
          		// 当前位置的值是null
            if ((a = as[(n - 1) & h]) == null) {
                // 锁的标志位 == 0 ,没有加锁 
                if (cellsBusy == 0) {       // Try to attach new Cell
                    Cell r = new Cell(Double.doubleToRawLongBits(x));
                    // 加锁
                    if (cellsBusy == 0 && casCellsBusy()) {
                        boolean created = false;
                        try {               // Recheck under lock
                            Cell[] rs; int m, j;
                        // 数组初始化过了 && 当前位置的值不是null
                            if ((rs = cells) != null &&
                                (m = rs.length) > 0 &&
                                rs[j = (m - 1) & h] == null) {
                                  // 给数组的位置设置为  之前设置过的cell对象
                                rs[j] = r;
                                // 创建成功
                                created = true;
                            }
                        } finally {
                            // 解锁
                            cellsBusy = 0;
                        }
                        if (created)
                            break;
                        continue;           // Slot is now non-empty
                    }
                }
                // 有人加锁了,发生了冲突
                collide = false;
            }
            // 分支1.2
          	 // 没有发生竞争
            else if (!wasUncontended)       // CAS already known to fail
                 // 此时是发生了竞争
                wasUncontended = true;      // Continue after rehash
           // 分支1.3
          		// cas 的方式更新 此位置的值, cas 失败表示有线程正在此位置执行操作
            else if (a.cas(v = a.value,
                           ((fn == null) ?
                            Double.doubleToRawLongBits
                            (Double.longBitsToDouble(v) + x) :
                            Double.doubleToRawLongBits
                            (fn.applyAsDouble
                             (Double.longBitsToDouble(v), x)))))
                break;
          // 分支1.4
              // n > cpu 的个数 或者 cells  发生了变化,表示 之前没有发生碰撞,不能扩容
            else if (n >= NCPU || cells != as)
                collide = false;            // At max size or stale
         // 分支1.5
           // 此时是发生了碰撞的 collide 被设置为 true 
            else if (!collide)
                collide = true;
             // 分支1.6 
           			// 扩容
          							// 没有被锁 && 成功加锁 
            else if (cellsBusy == 0 && casCellsBusy()) {
                try {
                  // 数组没有变化
                    if (cells == as) {      // Expand table unless stale
                      // as 数组长度扩大一倍
                        Cell[] rs = new Cell[n << 1];
                      // 元素直接赋值
                        for (int i = 0; i < n; ++i)
                            rs[i] = as[i];
                        cells = rs;
                    }
                } finally {
                    // 解锁
                    cellsBusy = 0;
                }
                 //没有冲突
                collide = false;
                 //扩容成功,继续循环
                continue;                   // Retry with expanded table
            }
          // 获取hash 值
            h = advanceProbe(h);
        }
// 分支 2
      // 此处数组没有进行初始化,此时进行初始化
      // 锁的标志为 == 0  &&  数组没有改变(多线程情况下该线程没有被其他线程初始化)  && cas 成功的把锁的标志位 设置为 1(枷锁流程)
        else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
            boolean init = false;
            try {   // Initialize table
                 // 加完锁之后再次判断一次  cells 数组没有发生过变化
                if (cells == as) {
                  // 数组 长度默认为2
                    Cell[] rs = new Cell[2];
                   // 给rs 赋值为 要加入的 x
                    rs[h & 1] = new Cell(Double.doubleToRawLongBits(x));
                   // 将 cells 数组变更为 rs
                    cells = rs;
                   // 初始化成功
                    init = true;
                }
            } finally {
              // 解锁
                cellsBusy = 0;
            }
            if (init)// 初始化成功
                break;    // 退出循环
        }
// 分支 3  cas 的方式 操作 base  , fn 函数式接口的方法  == null 默认加法,否则就是定义的方法
        else if (casBase(v = base,
                         ((fn == null) ?
                          Double.doubleToRawLongBits
                          (Double.longBitsToDouble(v) + x) :
                          Double.doubleToRawLongBits
                          (fn.applyAsDouble
                           (Double.longBitsToDouble(v), x)))))
            break;                          // Fall back on using base// 退出循环
    }
}
DoubleAccumulator

image-20231207001441645

public class DoubleAccumulator extends Striped64 implements Serializable {
    private static final long serialVersionUID = 7249069246863182397L;

    private final DoubleBinaryOperator function;
    private final long identity; // use long representation


    public DoubleAccumulator(DoubleBinaryOperator accumulatorFunction,double identity) {
        this.function = accumulatorFunction;
        base = this.identity = Double.doubleToRawLongBits(identity);
    }

  
 
    public void accumulate(double x) {
        Cell[] as; long b, v, r; int m; Cell a;
        // 数组存在 || (对 base 进行 cas的 运算操作成功  && 对base 进行cas 更新操作失败  )
        if ((as = cells) != null ||(r = Double.doubleToRawLongBits(function.applyAsDouble (Double.longBitsToDouble(b = base), x))) != b  && !casBase(b, r)) {
            boolean uncontended = true;
// 数组为 空 || 数组被初始化但是 数组的长度 < 0  || 当前位置的值为 null  ||  (对该位置的值进行cas 运算失败  || 对该值进行cas 更新失败)
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended =(r = Double.doubleToRawLongBits(function.applyAsDouble(Double.longBitsToDouble(v = a.value), x))) == v ||a.cas(v, r)))
                doubleAccumulate(x, function, uncontended);
        }
    }


    public double get() {
        Cell[] as = cells; Cell a;
        double result = Double.longBitsToDouble(base);
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    result = function.applyAsDouble
                        (result, Double.longBitsToDouble(a.value));
            }
        }
        return result;
    }


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


    public double getThenReset() {
        Cell[] as = cells; Cell a;
        double result = Double.longBitsToDouble(base);
        base = identity;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null) {
                    double v = Double.longBitsToDouble(a.value);
                    a.value = identity;
                    result = function.applyAsDouble(result, v);
                }
            }
        }
        return result;
    }


    public String toString() {
        return Double.toString(get());
    }

    public double doubleValue() {
        return get();
    }


    public long longValue() {
        return (long)get();
    }

    public int intValue() {
        return (int)get();
    }

    public float floatValue() {
        return (float)get();
    }

    private static class SerializationProxy implements Serializable {
        private static final long serialVersionUID = 7249069246863182397L;

        private final double value;

        private final DoubleBinaryOperator function;

        private final long identity;

        SerializationProxy(DoubleAccumulator a) {
            function = a.function;
            identity = a.identity;
            value = a.get();
        }


        private Object readResolve() {
            double d = Double.longBitsToDouble(identity);
            DoubleAccumulator a = new DoubleAccumulator(function, d);
            a.base = Double.doubleToRawLongBits(value);
            return a;
        }
    }


    private Object writeReplace() {
        return new SerializationProxy(this);
    }


    private void readObject(java.io.ObjectInputStream s)
        throws java.io.InvalidObjectException {
        throw new java.io.InvalidObjectException("Proxy required");
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/224102.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

工业4G路由器助力轨道交通城市地铁实现数字化转型

随着城市的科技不断发展&#xff0c;地铁系统的智能化程度也在不断提高。地铁闸机的网络部署已经成为地铁建设中必不可少环节。而4G路由器作为地铁闸机的网络通讯设备&#xff0c;助力轨道交通地铁闸机实现数字化转型。 工业4G路由器在地铁系统光纤宽带网络遇到故障或其他问题…

2023 金砖国家职业技能大赛网络安全省赛二三阶段样题(金砖国家未来技能挑战赛)

2023 金砖国家职业技能大赛网络安全省赛二三阶段样题&#xff08;金砖国家未来技能挑战赛&#xff09; 第二阶段&#xff1a; 安全运营 **背景&#xff1a;**作为信息安全技术人员必须能够掌握操作系统加固与安全管控、防火 墙一般配置、常见服务配置等相关技能&#xff0c;利…

java WebSocket带参数处理使用

1、webSocket实现代码 Component public class WebSocketStompConfig {//这个bean的注册,用于扫描带有ServerEndpoint的注解成为websocket// ,如果你使用外置的tomcat就不需要该配置文件Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpoi…

微信小程序 -- ios 底部小黑条样式问题

问题&#xff1a; 如图&#xff0c;ios有的机型底部伪home键会显示在按钮之上&#xff0c;导致点击按钮的时候误触 解决&#xff1a; App.vue <script>export default {wx.getSystemInfo({success: res > {let bottomHeight res.screenHeight - res.safeArea.bott…

Xcode15 Library ‘iconv.2.4.0‘ not found

Xcode 15运行老代码报错&#xff1a;Library iconv.2.4.0 not found 解决&#xff1a; TARGETS-->Bulid Phases --> Link Binary With Libraries 添加一个“Libiconv.tbd”, 同时把原来的 “libiconv.2.4.0.tbd”删除&#xff08;一定要删除&#xff0c;不然运行还是…

玩转大数据9:机器学习在大数据分析中的应用

1. 引言 在大数据时代&#xff0c;机器学习在大数据分析中扮演着至关重要的角色。本文介绍机器学习在大数据分析中的重要性和应用场景&#xff0c;并探讨Java中可用的机器学习库和框架。 2. 机器学习的基本概念和算法 机器学习是当今人工智能领域的一个关键分支&#xff0c;…

B 站基于 StarRocks 构建大数据元仓

作者&#xff1a;bilibili 大数据高级开发工程师 杨洋 B站大数据元仓是一款用来观测大数据引擎运行情况、推动大作业治理的系统诊断产品。经过调研和性能测试&#xff0c;大数据元仓最终以 StarRocks 为技术底座&#xff0c;从实际的应用效果来看&#xff0c;大部分查询都能在几…

在 Node-RED 中引入 ECharts 实现数据可视化

Node-RED 提供了强大的可视化工具&#xff0c;而通过引入 ECharts 图表库&#xff0c;您可以更直观地呈现和分析数据。在这篇博客中&#xff0c;我们将介绍两种在 Node-RED 中实现数据可视化的方法&#xff1a;一种是引入本地 ECharts 库&#xff0c;另一种是直接使用 CDN&…

【Hadoop_01】Hadoop介绍与安装

1、Hadoop、HDFS、YARN介绍&#xff08;1&#xff09;Hadoop简介与优势&#xff08;2&#xff09;Hadoop组成&#xff08;3&#xff09;HDFS概述&#xff08;4&#xff09;YARN概述&#xff08;5&#xff09;MapReduce概述 2、安装&#xff08;1&#xff09;Centos7.5软硬件安装…

如何在任何STM32上面安装micro_ros

就我知道的&#xff1a;micro-ros只能在特定的昂贵的开发板上面运行&#xff0c;但是偶然发现了这个文章&#xff0c;似乎提供了一个全新的方式来在ros2和单片机之间通讯&#xff0c;如果能够这样肯定也能够提高效率&#xff0c;但即使不行&#xff0c;使用串口库也应该比较简单…

Android Camera 预览尺寸的选取与旋转角度的设定

Camera 在音视频的世界中, 就像我们的眼睛一样, 是负责图像采集的入口, 要想实现一款高可用的 Camera, 我认为需要关注以下几个方面 相机的预览相机的拍摄相机的录制 OpenGL ES 主要用于图像的渲染, 因此这里主要聚焦于相机的预览, 想要让相机的数据呈现到我们屏幕上, 主要有…

Java 将word转为PDF的三种方式和处理在服务器上下载后乱码的格式

我这边是因为业务需要将之前导出的word文档转换为PDF文件&#xff0c;然后页面预览下载这样的情况。之前导出word文档又不是我做的&#xff0c;所以为了不影响业务&#xff0c;只是将最后在输出流时转换成了PDF&#xff0c;当时本地调用没什么问题&#xff0c;一切正常&#xf…

「X」Embedding in NLP|Token 和 N-Gram、Bag-of-Words 模型释义

ChatGPT&#xff08;GPT-3.5&#xff09;和其他大型语言模型&#xff08;Pi、Claude、Bard 等&#xff09;凭何火爆全球&#xff1f;这些语言模型的运作原理是什么&#xff1f;为什么它们在所训练的任务上表现如此出色&#xff1f; 虽然没有人可以给出完整的答案&#xff0c;但…

c++新经典模板与泛型编程:const修饰符的移除与增加

const修饰符的移除 让你来写移除const修饰符&#xff0c;你会怎么样来写&#xff1f; &#x1f602;&#x1f602;trait类模板&#xff0c;如下 #include <iostream>// 泛化版本 template<typename T> struct RemoveConst {using type T; };// 特化版本 template…

案例059:基于微信小程序的在线投稿系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

18、XSS——cookie安全

文章目录 1、cookie重要字段2、子域cookie机制3、路径cookie机制4、HttpOnly Cookie机制5、Secure Cookie机制6、本地cookie与内存cookie7、本地存储方式 一般来说&#xff0c;同域内浏览器中发出的任何一个请求都会带上cookie&#xff0c;无论请求什么资源&#xff0c;请求时&…

ubuntu中显卡驱动,cuda,cudnn安装

1. 在ubuntu中安装显卡驱动 参考&#xff1a;https://blog.csdn.net/m0_37605642/article/details/119651996 2.在ubuntu中安装cuda 参考&#xff1a;https://blog.csdn.net/m0_61431544/article/details/127007300 2.1 安装cuda cuda官网&#xff1a; https://developer.n…

配置端口安全示例

组网需求 如图1所示&#xff0c;用户PC1、PC2、PC3通过接入设备连接公司网络。为了提高用户接入的安全性&#xff0c;将接入设备Switch的接口使能端口安全功能&#xff0c;并且设置接口学习MAC地址数的上限为接入用户数&#xff0c;这样其他外来人员使用自己带来的PC无法访问公…

基于单片机出租车计价器控制系统

**单片机设计介绍&#xff0c;基于单片机出租车计价器控制系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的出租车计价器控制系统是一个用于控制和管理出租车费用计算的电子设备。下面是一个简单的系统设计介绍&…

【若依系列】1.项目修改成自己包名并启动服务

项目下载地址&#xff1a; 分离版本 https://gitee.com/y_project/RuoYi-Vue 修改工具下载 https://gitee.com/lpf_project/common-tools 相关截图&#xff1a; 1.项目结构&#xff1a; 2.修改包名工具&#xff1a; 工具截图&#xff0c;根据对应提示自定义修改即可&#x…