一、什么是原子类

  1. 一个操作是不可中断的,即使是多线程的情况下也可以保证
  2. 在java中原子类都被保存在 java.util.concurrent.atomatic

二、原子类的作用

  1. 原子类的作用和锁类似,是为了保证并发情况下线程安全,不过原子类相比于锁,有一定的优势。
  2. 粒度更细,原子变量可以把竞争范围缩小到变量级别,这是我们可以获得的最细粒度的情况了,通常锁的粒度都要大于原子变量的粒度。
  3. 效率更高,通常使用原子类的效率比使用锁的效率更高,除了高度竞争的情况。

三、原子类纵览

Atomic* 基本类型原子类  

AtomicInteger

AtomicLong

AtomicBoolean

Atomic* Array数组类型原子类      

AtomicIntegerArray

AtomicLongArray

AtomicReferenceArray

Atomic*Reference 引用类型原子类

AtomicReference

AtomicStampedReference

AtomicMarkableReference

Atomic*FieldUpdate 升级类型原子类

AtomicIntegerFieldupdater

AtomicLongFieldUpdater

AtomicReferenceFieldUpdater

Adder累加器

LongAdder、DoubleAdder

Accumulator累加器

LongAccumulator

DoubleAccumulator

四、Atomic*基本原子类

以AtomicInteger为例

AtomicIntege 常用方法

  • get():获取当前的值
  • getAndSet():获取当前值big设置新的值
  • getAndIncrement():获取当前的值并自增
  • getAndDecrement():获取当前的值并自减
  • getAndAdd(int delta):获取当前的值,并加上预期的值
  • compareAndSet(int expect,int update):如果当前的数组等于预期值,则以原子方式将该值设置为输入值(update)

五、Atomic*Reference 引用原子类

AtmoicReference:AtmoicReference类的作用和AtomicInteger并没有本质区别,AtomicInteger可以让一个整数保证原子性,AtmoicReference可以让一个对象保证原子性,当然,AtmoicReference功能明显比AtomicInteger强,因为一个对象里面可以包含很多属性。用法和AtomicInteger类似

六、把普通变量升级为具有原子功能的变量

AtomicIntegerFieldUpdater对普通变量进行升级,使用注意点如下:

  • 可见范围(private的就不可以了)
  • 不支持static变量

七、Adder累加器

高并发下LongAdder比AtomicLong效率高,不过本质是空间换时间

竞争激烈的时候,LongAdder把不同线程对应到不同的Cell上进行修改,降低了冲突概率,是多段锁的理念,提高了并发性

LongAdders效率高的原因

AtomicLong每个线程对变量进行修改时都要从本地内存flush到主内存中,然后再从主内存refrsh到另一个线程的本地内存中,如下图

java 原子自增 java原子类的使用场景_数组

LongAdder就不存在AtomicLong的那种情况,每个线程将变量在自己的内存中做计算,不需要同步到主内存中,如下图

java 原子自增 java原子类的使用场景_数组_02

如图中所示,第一个线程的计数器的值也就是ctr' = 1 为1的时候,可能线程2的计数器 ctr'' 的数值已经为3了,他们之间并不存在竞争的关系,所以在加和的过程中,根本不需要同步机制,也不需要flush和refresh,这里也没有一个公共的counter来给所有线程统一计数 

LongAdder原理

在内部,这个LongAdder的实现原理和AtomicLong是有不同的,刚才的AtomicLong的实现原理是每一次加法都需要做同步,所以在高并发的时候回导致冲突比较多也就降低了效率

LongAdder每个线程会有自己的一个计数器,仅用来在自己的线程内计数,这样一来就不会和其他线程的计数器干扰

LongAdder将每个线程内的计数器累计分析

LongAdder 引入了分段累加的概念,内部有一个base变量和一个cell[] 数组共同参与计数

base变量:竞争不激烈,直接累加到该变量上

cell[]数组:竞争激烈,各个线程分散累加到自己的槽cell[i] 中

java 原子自增 java原子类的使用场景_AtomicInteger_03

 

由上面的源码可以看出,当在做累计时cells[i]的值随时可能发生改变,所以最终的求和不是十分的精确

AtomicLong和LongAdder对比

  1. 在低争用下,AtomicLong和LongAdder这两个类具有相似的特征,但是在竞争激烈的情况下,LongAdder的预期吞吐量要高得多,但是要消耗更多的空间
  2. LongAdder 适合的场景是统计求和和计数的场景,而且LongAdder基本只提供了add方法,而AtomicLong还具有cas方法

八、Accumulator

Accumulator就是对Adder的升级 不仅可以做加法操作还可以做其他操作例如:乘法,求最大最小值等