javaSE 并发包 原子变量操作类

原子变量操作类

使用场景

在使用volatile修饰变量后,能保证内存可见性,但是并不能保证原子性操作,因此在JUC中补充了一些原子性操作类:AtomicInteger、AtomicLong、AtomicBoolean等。其内部值都是volatile修饰的。

源码分析

AtomicLong是原子性递增或者递减类,其内部使用Unsafe类来实现。

 

方法

作用

AtomicLong()

 

构造函数

 

AtomicLong(long initialValue)

创建值为initialValue的AtomicLong对象

 

final void set(long newValue)

以原子方式设置当前值为newValue。

 

final long get()

获取当前值

 

final long decrementAndGet()

以原子方式将当前值减 1,并返回减1后的值。等价于“--num”

final long getAndDecrement()

以原子方式将当前值减 1,并返回减1前的值。等价于“num--”

final long incrementAndGet()

 

以原子方式将当前值加 1,并返回加1后的值。等价于“++num”

final long getAndIncrement()

以原子方式将当前值加 1,并返回加1前的值。等价于“num++”

final long addAndGet(long delta)

以原子方式将delta与当前值相加,并返回相加后的值。

final long getAndAdd(long delta)

以原子方式将delta添加到当前值,并返回相加前的值。

final boolean compareAndSet(long expect, long update)

如果当前值 == expect,则以原子方式将该值设置为update。成功返回true,否则返回false,并且不修改原值。

final long getAndSet(long newValue)

 

 以原子方式设置当前值为newValue,并返回旧值。

int intValue()

返回当前值对应的int值

 

long longValue()

获取当前值对应的long值

 

float floatValue()

以 float 形式返回当前值

 

double doubleValue()

以 double 形式返回当前值

 

final void lazySet(long newValue)

 

最后设置为给定值。延时设置变量值,这个等价于set()方法,但是由于字段是volatile类型的,因此次字段的修改会比普通字段(非volatile字段)有稍微的性能延时(尽管可以忽略),所以如果不是想立即读取设置的新值,允许在“后台”修改值,那么此方法就很有用。如果还是难以理解,这里就类似于启动一个后台线程如执行修改新值的任务,原线程就不等待修改结果立即返回(这种解释其实是不正确的,但是可以这么理解)。

final boolean weakCompareAndSet(long expect, long update)

 

如果当前值 == 预期值,则以原子方式将该设置为给定的更新值。JSR规范中说:以原子方式读取和有条件地写入变量但不 创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。大意就是说调用weakCompareAndSet时并不能保证不存在happen-before的发生(也就是可能存在指令重排序导致此操作失败)。但是从Java源码来看,其实此方法并没有实现JSR规范的要求,最后效果和compareAndSet是等效的,都调用了unsafe.compareAndSwapInt()完成操作。

 

 

LongAdder

使用AtomicLong通过CAS提供的非阻塞的原则操作,相比于阻塞算法的同步器来说性能上是一大提升,但是使用AtomicLong时,在高并发的情况下,多个线程会同时竞争同一个原子变量,但是由于只有一个线程的CAS操作会成功,大量的线程失败后会通过无线循环的不断进行自旋尝试CAS操作,这也会造成CPU资源的浪费。因此,JDK8新增了一个原子性递增递减类LongAdder来克服高并发下的AtomicLong的缺点。

 

LongAdder的原理是,在LongAdder内部维护一个base和多个Cell变量,每个cell里面都有一个初始化为0的long型变量。在高并发的情况下争夺单个变量更新操作就会减少,变相的减少了争夺共享资源的并发量。

多个线程竞争同一个Cell失败后,并不是在当前的Cell上自旋CAS操作,而且去争夺其他Cell进行CAS操作,这个增加了当前线程重试CAS操作成功的可能性。

LongAdder的实际值为base值加所以Cell累加后的值。

LongAdder维护的是一个延迟初始化的原子性更新数组Cells(默认下Cell数组是null)和一个基值变量base,因为Cells内存相对较大,所以是惰性加载。

当一开始Cells数组是null并且并发量较小时,所有的累加操作是对base变量进行的,初始化时Cells的大小为2,大小要保持为2的N次方。

大多数的孤立的多个原子操作都是分散在内存中,公用一个内存行(Cache)的几率小,单原子性数组元素的内存地址是连续的,原子性数组多个元素经常共享内存行(cache),因此使用@sun.misc.Contended注解对Cell进行字节填充。使用可以避免伪共享,提升性能。

LongAdder源码分析

public class LongAdder extends Striped64 implements Serializable {}可以看出LongAdder是继承Striped64 ,Striped64 中维护者三个变量:

transient volatile Cell[] cells//Cell数组

transient volatile long base//base基础值,默认为0

transient volatile int cellsBusy。//cellsBusy用来实现自旋锁,状态值为0和1

 

Cell的构造

 @sun.misc.Contended static final class Cell {

        volatile long value;

        Cell(long x) { value = x; }

}

从以上源码可以看出Cell使用 @sun.misc.Contended注解填充避免伪共享,且Cell的实际值value为volatile 修饰,使用CAS保证原子性操。

 

LongAccumulator

LongAdder类是LongAccumulator的一个特例,LongAccumulator比LongAdder的功能更加强大,可以自定义累加规则。

LongAccumulator相比于LongAdder,可以为累积器提供非0的初始值,后者只能提供默认的0值,另外前者还可以指定累加规则,比如不进行累加而进行相乘,只需要在构造LongAccumulator时传入自定义的双目运算器即可,后者为内置累加规则。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值