AtomicLong
JUC并发包中包含有 Atomiclnteger、AtomicLong和AtomicBoolean等原子性操作类,它们的原理类似。
主要是通过CAS+自旋,用非阻塞的方式原子地修改原子类的数值,性能较好。
但是在高并发情况下 AtomicLong 还会存在性能问题,JDK 8 提供了一个在高并发下性能更好的 LongAdder 类。
使用AtomicLong时,在高并发下大量线程会同时去竞争更新同个原子变量,但是由于同时只有一个线程的CAS操作会成功,这就造成了大量线程竞争失败后,会通过无限循环不断进行自旋尝试CAS的操作,而这会白白浪费CPU资源。
LongAdder
JDK8新增了一个原子性递增或者递减类LongAdder用来克服在高并发下使用AtomicLong的缺点。
AtomicLong的性能瓶颈是由于过多线程同时去竞争一个变量的更新而产生的,如果把一个变量分解为多个变量,让同样多的线程去竞争多个资源,就解决了性能问题,类似于分段锁的思想。
- LongAdder维护了一个延迟初始化的原子性更新数组(默认情况 下 Cell 数组是 null)和一个基值变量base。
- 使用LongAdder时,则是在内部维护多个Cell变量,每个Cell里面有一个初始值为0的long型变量。
- cas失败会换一个cell重试。多个线程在争夺同一个Cell原子变量时如果失败了,它并不是在当前Cell变量上一直自旋CAS重试,而是尝试在其他Cell的变量上进行CAS尝试,这个改变增加了当前线程重试CAS成功的可能性。
- 获取LongAdder当前值时,是把所有Cell变量的value值累加后再加上base返回的。

如何在JDK8之前的版本使用LongAdder?
阅读阿里开源限流器sentinel,可以看到它并没有使用jdk提供的LongAdder,而是为了兼容之前的jdk版本,自己写了一个LongAdder类,然后通过反射的方式调用Unsafe类来实现相关功能的。