原子变量操作类
使用场景
在使用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时传入自定义的双目运算器即可,后者为内置累加规则。