原子类的分析


原子类的分析

原子类是什么?

原子类是操作要么是成功要么是失败,不能被中断的类。在java中是在java.util.concurrent.atomic下。与锁对比

  • 粒度更细
  • 效率更高

原子类有哪些?

分类 类名
Atomic* 基本类型原子类 AtomicInteger、AtomicLong、AtomicBoolean
Atomic*Array 数组原子类 AtomicIntgerArray、AtomicLongArray、AtoimcReferenceArray
Atomic*Reference 引用类型 AtomicReference、AtomicStampedReference、AtomicMarkableReference
Atomic*FieldUpdater 升级类型原子类 AtomicIntegerfieldupdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
Adder 累加器 LongAdder、DoubleAdder
Accumulator 积累器 LongAccumulator、DoubleAccumulator

Atomic* 基本类型原子类

Atomic* 基本原子类,主要是AtomicInteger、AtomicLong、AtomicBoolean
AtomicInteger 类常用方法

  • public final int get() //获取当前的值
  • public final int getAndSet(int newValue) //获取当前的值,并设置新的值
  • public final int getAndIncrement() //获取当前的值,并自增
  • public final int getAndDecrement() //获取当前的值,并自减
  • public final int getAndAdd(int delta) //获取当前的值,并加上预期的值

Atomic*Array 数组类型原子类

Atomic*Array 数组类型原子类,数组里的元素,都可以保证其原子性,比如 AtomicIntegerArray 相当于把 AtomicInteger 聚合起来,组合成一个数组。主要是AtomicIntegerArray:整形数组原子类、AtomicLongArray:长整形数组原子类、AtomicReferenceArray:引用类型数组原子类

Atomic*Reference 引用类型原子类

  • AtomicReference 引用类型原子类,可以让一个对象保证原子性。
  • AtomicStampedReference:它是对 AtomicReference 的升级,在此基础上还加了时间戳,用于解决 CAS 的 ABA 问题
  • AtomicMarkableReference 和AtomicReference类似,但是多一个boolean类型可以用来标记是否删除了对象

Atomic*FieldUpdater 原子更新器

  • AtomicIntegerFieldUpdater:原子更新整型的更新器
  • AtomicLongFieldUpdater:原子更新长整型的更新器;
  • AtomicReferenceFieldUpdater:原子更新引用的更新器。
    可以将一个原本不具有原子类操作的类型升级成为一个具有原子的类。

Adder 加法器

  • LongAdder
  • DubleAdder

Accumulator 积累器

  • LongAccumulator
  • DoubleAccumulator

底层原理

用AtomicIntger类来分析

public final int addAndGet(int delta) {
    return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}

用的是unsafe.getAndAddInt方法,这是一个调用硬件进行CAS的方法,通过自旋和CAS保证了并发的安全性

AtomicIntger在高并发下的性能问题

AtomicIntger底层是用一个volatile进行修饰value字段,因此在高并发的场景下,线程会不断的进行flushreflush
因此LongAdder是更新方案,采用的是base即value和call[]数组用分段的思想,在并发激烈的场景下会加线程需要的资源分配到不同的call中去,在需要sum等操作时遍历call[]数组和base从而得出值

原子类和volatile的区别

原子类是对类的一系列操作进行了封装,让这一系列的操作成为一个原子性的动作。volatile修饰的变量是在内存模型上对其他线程透明;
一个表示的语义是并发安全,一个表示的语义是线程间的可见性;
两者的适用场景也不一样,volatile适用于可见性方面,原子类适用于对一系列操作具有的原子性的要求;

LongAccumulator

LongAccumulator是对longAdder的一个升级,提供了自定义函数的功能,构造器如下

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

LongBinaryOperator是一个实现值如何处理的类

@FunctionalInterface
public interface LongBinaryOperator {

    /**
     * Applies this operator to the given operands.
     *
     * @param left the first operand
     * @param right the second operand
     * @return the operator result
     */
    long applyAsLong(long left, long right);
}

测试用例如下

CountDownLatch flag = new CountDownLatch(10);
ExecutorService threadPool = new ThreadPoolExecutor(10, 10, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
LongAccumulator accumulator = new LongAccumulator((x, y) -> x + y, 0);
IntStream.range(1, 11).forEach(i -> threadPool.submit(() -> {
    System.out.println(Thread.currentThread().getName() + ":executer");
    accumulator.accumulate(i);
    flag.countDown();
}));
flag.await();
System.out.println(accumulator.getThenReset());

运行结果:
3zjqL8.png

适用场景:

  • 大量的计算并且需要并行计算
  • 线程之间执行不需要顺序执行

  TOC