Java
Java多线程

Java多线程 11 - AtomicLong详解

简介:AtomicLong是作用是对长整形进行原子操作。在32位操作系统中,64位的long和double变量由于会被JVM当作两个分离的32位来进行操作,所以不具有原子性。而使用AtomicLong能让long的操作保持原子型。

1. Atomic类概述

根据修改的数据类型,可以将JUC包中的原子操作类可以分为4类。

  1. 基本类型: AtomicInteger、AtomicLong、AtomicBoolean。
  2. 数组类型: AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray。
  3. 引用类型: AtomicReference、AtomicStampedRerence、AtomicMarkableReference。
  4. 对象的属性修改类型: AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater。

这些类存在的目的是对相应的数据进行原子操作。所谓原子操作,是指操作过程不会被中断,保证数据操作是以原子方式进行的。

2. AtomicLong详解

AtomicLong是作用是对长整形进行原子操作。在32位操作系统中,64位的long和double变量由于会被JVM当作两个分离的32位来进行操作,所以不具有原子性。而使用AtomicLong能让long的操作保持原子型。

2.1. AtomicLong API列表

  • // 构造函数
  • AtomicLong()
  • // 创建值为initialValue的AtomicLong对象
  • AtomicLong(long initialValue)
  • // 以原子方式设置当前值为newValue。
  • final void set(long newValue)
  • // 获取当前值
  • final long get()
  • // 以原子方式将当前值减 1,并返回减1后的值。等价于--num
  • final long decrementAndGet()
  • // 以原子方式将当前值减 1,并返回减1前的值。等价于num--
  • final long getAndDecrement()
  • // 以原子方式将当前值加 1,并返回加1后的值。等价于++num
  • final long incrementAndGet()
  • // 以原子方式将当前值加 1,并返回加1前的值。等价于num++
  • final long getAndIncrement()
  • // 以原子方式将delta与当前值相加,并返回相加后的值。
  • final long addAndGet(long delta)
  • // 以原子方式将delta添加到当前值,并返回相加前的值。
  • final long getAndAdd(long delta)
  • // 如果当前值等于expect,则以原子方式将该值设置为update。成功返回true,否则返回false,并且不修改原值。
  • final boolean compareAndSet(long expect, long update)
  • // 以原子方式设置当前值为newValue,并返回旧值。
  • final long getAndSet(long newValue)
  • // 以原子方式使用给定的方法获取的值更新当前的值,并返回旧值,自JDK1.8起提供。LongUnaryOperator会被多次调用
  • final long getAndUpdate(LongUnaryOperator updateFunction)
  • // 以原子方式使用给定的方法获取的值更新当前的值,并返回新值,自JDK1.8起提供。LongUnaryOperator会被多次调用
  • final long updateAndGet(LongUnaryOperator updateFunction)
  • // 以原子方式使用给定的方法获取的值将x作为参数更新当前的值,并返回旧值,自JDK1.8起提供。LongUnaryOperator会被多次调用
  • final long getAndAccumulate(long x, LongBinaryOperator accumulatorFunction)
  • // 以原子方式使用给定的方法获取的值将x作为参数更新当前的值,并返回旧值,自JDK1.8起提供。LongUnaryOperator会被多次调用
  • final long accumulateAndGet(long x, LongBinaryOperator accumulatorFunction)
  • // 返回当前值对应的int值
  • int intValue()
  • // 获取当前值对应的long值
  • long longValue()
  • // 以 float 形式返回当前值
  • float floatValue()
  • // 以 double 形式返回当前值
  • double doubleValue()
  • // 最后设置为给定值。延时设置变量值,这个等价于set()方法,但是由于字段是volatile类型的,因此次字段的修改会比普通字段(非volatile字段)有稍微的性能延时(尽管可以忽略),所以如果不是想立即读取设置的新值,允许在后台修改值,那么此方法就很有用。如果还是难以理解,这里就类似于启动一个后台线程如执行修改新值的任务,原线程就不等待修改结果立即返回(这种解释其实是不正确的,但是可以这么理解)。
  • final void lazySet(long newValue)
  • // 如果当前值等于预期值,则以原子方式将该设置为给定的更新值。JSR规范中说:以原子方式读取和有条件地写入变量但不 创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。大意就是说调用weakCompareAndSet时并不能保证不存在happen-before的发生(也就是可能存在指令重排序导致此操作失败)。但是从Java源码来看,其实此方法并没有实现JSR规范的要求,最后效果和compareAndSet是等效的,都调用了unsafe.compareAndSwapLong()完成操作。
  • final boolean weakCompareAndSet(long expect, long update)

2.2. AtomicLong源码解析

AtomicLong源码源码如下(基于JDK 1.8.0_101):

  • package java.util.concurrent.atomic;
  • import java.util.function.LongUnaryOperator;
  • import java.util.function.LongBinaryOperator;
  • import sun.misc.Unsafe;
  • public class AtomicLong extends Number implements java.io.Serializable {
  • // 支持序列化
  • private static final long serialVersionUID = 1927816293512124184L;
  • // 用于CAS操作的Unsafe类
  • private static final Unsafe unsafe = Unsafe.getUnsafe();
  • private static final long valueOffset;
  • /**
  • * 用于记录当前JVM是否支持对long类型的lockless compareAndSwap
  • */
  • static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
  • private static native boolean VMSupportsCS8();
  • static {
  • try {
  • valueOffset = unsafe.objectFieldOffset
  • (java.util.concurrent.atomic.AtomicLong.class.getDeclaredField("value"));
  • } catch (Exception ex) {
  • throw new Error(ex);
  • }
  • }
  • // 用于存储值的属性,volatile保持线程可见性
  • private volatile long value;
  • // 通过传入一个long类型参数构造AtomicLong对象
  • public AtomicLong(long initialValue) {
  • value = initialValue;
  • }
  • // 无参构造默认是0
  • public AtomicLong() {
  • }
  • // 获取当前的值
  • public final long get() {
  • return value;
  • }
  • // 设置当前的值
  • public final void set(long newValue) {
  • value = newValue;
  • }
  • // 延时设置变量值,最后设置为给定值。
  • public final void lazySet(long newValue) {
  • unsafe.putOrderedLong(this, valueOffset, newValue);
  • }
  • // 原子性地设置新值并返回旧值
  • public final long getAndSet(long newValue) {
  • return unsafe.getAndSetLong(this, valueOffset, newValue);
  • }
  • // 如果当前值与expect相等,则以原子方式将该值设置为update。成功返回true,否则返回false,并且不修改原值。
  • public final boolean compareAndSet(long expect, long update) {
  • return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
  • }
  • /**
  • * 如果当前值等于预期值,则以原子方式将该设置为给定的更新值。
  • * JSR规范中说:
  • * 以原子方式读取和有条件地写入变量但不创建任何happen-before 排序,因此不提供与除weakCompareAndSet目标外任何变量以前或后续读取或写入操作有关的任何保证。
  • * 大意就是说调用weakCompareAndSet时并不能保证不存在happen-before的发生(也就是可能存在指令重排序导致此操作失败)。
  • * 但是从Java源码来看,其实此方法并没有实现JSR规范的要求,最后效果和compareAndSet是等效的,都调用了unsafe.compareAndSwapInt()完成操作。
  • */
  • public final boolean weakCompareAndSet(long expect, long update) {
  • return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
  • }
  • // 以原子方式将当前值加1,并返回加1前的值。等价于num++
  • public final long getAndIncrement() {
  • return unsafe.getAndAddLong(this, valueOffset, 1L);
  • }
  • // 以原子方式将当前值减1,并返回减1前的值。等价于num--
  • public final long getAndDecrement() {
  • return unsafe.getAndAddLong(this, valueOffset, -1L);
  • }
  • // 以原子方式将delta添加到当前值,并返回相加前的值。
  • public final long getAndAdd(long delta) {
  • return unsafe.getAndAddLong(this, valueOffset, delta);
  • }
  • // 以原子方式将当前值加1,并返回加1后的值。等价于++num
  • public final long incrementAndGet() {
  • return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
  • }
  • // 以原子方式将当前值减1,并返回减1后的值。等价于--num
  • public final long decrementAndGet() {
  • return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
  • }
  • // 以原子方式将delta添加到当前值,并返回相加后的值。
  • public final long addAndGet(long delta) {
  • return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
  • }
  • /**
  • * 以原子方式使用给定的方法获取的值更新当前的值,并返回旧值,自JDK1.8起提供
  • * LongUnaryOperator会被多次调用
  • */
  • public final long getAndUpdate(LongUnaryOperator updateFunction) {
  • long prev, next;
  • do {
  • prev = get();
  • next = updateFunction.applyAsLong(prev);
  • } while (!compareAndSet(prev, next));
  • return prev;
  • }
  • /**
  • * 以原子方式使用给定的方法获取的值更新当前的值,并返回新值,自JDK1.8起提供
  • * LongUnaryOperator会被多次调用
  • */
  • public final long updateAndGet(LongUnaryOperator updateFunction) {
  • long prev, next;
  • do {
  • prev = get();
  • next = updateFunction.applyAsLong(prev);
  • } while (!compareAndSet(prev, next));
  • return next;
  • }
  • /**
  • * 以原子方式使用给定的方法获取的值将x作为参数更新当前的值,并返回旧值,自JDK1.8起提供
  • * LongBinaryOperator会被多次调用
  • */
  • public final long getAndAccumulate(long x, LongBinaryOperator accumulatorFunction) {
  • long prev, next;
  • do {
  • prev = get();
  • next = accumulatorFunction.applyAsLong(prev, x);
  • } while (!compareAndSet(prev, next));
  • return prev;
  • }
  • /**
  • * 以原子方式使用给定的方法获取的值将x作为参数更新当前的值,并返回旧值,自JDK1.8起提供
  • * LongBinaryOperator会被多次调用
  • */
  • public final long accumulateAndGet(long x, LongBinaryOperator accumulatorFunction) {
  • long prev, next;
  • do {
  • prev = get();
  • next = accumulatorFunction.applyAsLong(prev, x);
  • } while (!compareAndSet(prev, next));
  • return next;
  • }
  • // 返回当前值对应的String
  • public String toString() {
  • return Long.toString(get());
  • }
  • // 返回当前值对应的int值
  • public int intValue() {
  • return (int) get();
  • }
  • // 返回当前值对应的long值
  • public long longValue() {
  • return get();
  • }
  • // 返回当前值对应的float值
  • public float floatValue() {
  • return (float) get();
  • }
  • // 返回当前值对应的double值
  • public double doubleValue() {
  • return (double) get();
  • }
  • }

在JDK1.8的源码中基本所有的操作都以Unsafe类进行了二次封装,我们查看getAndIncrement()incrementAndGet()的源代码:

  • // 以原子方式将当前值加1,并返回加1前的值。等价于num++
  • public final long getAndIncrement() {
  • return unsafe.getAndAddLong(this, valueOffset, 1L);
  • }
  • // 以原子方式将当前值加1,并返回加1后的值。等价于++num
  • public final long incrementAndGet() {
  • return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
  • }

可以发现,这两个方法都调用了Unsafe类操作的getAndAddLong(Object, long, long)方法,同时第二个参数传入了valueOffsetvalueOffset是定义在AtomicLong类中的一个字段,并且在类加载的时候直接进行初始化:

  • private static final long valueOffset;
  • private volatile long value;
  • static {
  • try {
  • valueOffset = unsafe.objectFieldOffset(AtomicLong.class.getDeclaredField("value"));
  • } catch (Exception ex) {
  • throw new Error(ex);
  • }
  • }

unsafe.objectFieldOffset(Field)方法用于获取某个字段相对Java对象的起始地址的偏移量,这个偏移量在下面将要介绍的Unsafe类中会介绍到。我们只需要明白valueOffset字段其实就是AtomicLong类中真正存储值的value字段在对象中的起始地址偏移量,Unsafe类会根据这个字段来访问AtomicLong类实例的value值。

对于getAndIncrement()incrementAndGet()方法中调用的Unsafe类实例的getAndAddLong(Object object, long valueOffset, long delta)方法反编译代码如下:

  • public final long getAndAddLong(Object object, long valueOffset, long delta) {
  • // 当前值
  • long current;
  • do {
  • // 获取AtomicLong的当前值
  • current = this.getLongVolatile(object, valueOffset);
  • } while(!this.compareAndSwapLong(object, valueOffset, current, current + delta));
  • return current;
  • }
  • public final native boolean compareAndSwapLong(Object object, long valueOffset, long except, long update);

其中this.getLongVolatile(object, valueOffset);正是根据传入的valueOffset在传入的对象object中访问对应起始地址偏移量的值,在这里就是访问AtomicLong类实例的value值。getAndAddLong(Object, long, long)先记录了旧值,然后进行循环操作,循环的条件中compareAndSwapLong(Object object, long valueOffset, long except, long update)操作,这个操作即是CAS操作,它的运行步骤如下:

  1. 首先获取object中起始地址偏移量为valueOffset的值,假设为now;
  2. 然后判断now与except是否相等;如果相等则以原子方式将now设置为update,成功返回true;否则返回false,并且不做任何修改。

综上分析,getAndAddLong(Object, long, long)方法的执行步骤如下:

  1. 刚进来的时候,先执行current = this.getLongVolatile(object, valueOffset);,将object对象中起始地址偏移量为valueOffset的值保存在current中;
  2. 然后使用compareAndSwapLong(Object object, long valueOffset, long except, long update)尝试将current值修改为current + delta,如果成功就结束循环,否则重复1 ~ 2步;
  3. 最后返回之前保存旧值的current变量。

3. AtomicLong示例

下面是对AtomicLong类型的常用操作的示例:

  • public class AtomicLongTest {
  • public static void main(String[] args) {
  • // 新建AtomicLong对象
  • AtomicLong atomicLong = new AtomicLong();
  • atomicLong.set(0x0123456789ABCDEFL);
  • // 转换类型
  • System.out.println("\n------------------------ 转换类型 ------------------------");
  • System.out.printf("%35s : 0x%016X\n", "get()", atomicLong.get());
  • System.out.printf("%35s : 0x%016X\n", "intValue()", atomicLong.intValue());
  • System.out.printf("%35s : 0x%016X\n", "longValue()", atomicLong.longValue());
  • System.out.printf("%35s : %s\n", "doubleValue()", atomicLong.doubleValue());
  • System.out.printf("%35s : %s\n", "floatValue()", atomicLong.floatValue());
  • // +1或-1操作
  • System.out.println("\n------------------------ +1或-1操作 ---------------------");
  • System.out.printf("%35s : 0x%016X\n", "getAndDecrement()", atomicLong.getAndDecrement());
  • System.out.printf("%35s : 0x%016X\n", "decrementAndGet()", atomicLong.decrementAndGet());
  • System.out.printf("%35s : 0x%016X\n", "getAndIncrement()", atomicLong.getAndIncrement());
  • System.out.printf("%35s : 0x%016X\n", "incrementAndGet()", atomicLong.incrementAndGet());
  • // +n操作
  • System.out.println("\n------------------------ +n操作 -------------------------");
  • System.out.printf("%35s : 0x%016X\n", "addAndGet(0x10)", atomicLong.addAndGet(0x10));
  • System.out.printf("%35s : 0x%016X\n", "getAndAdd(0x10)", atomicLong.getAndAdd(0x10));
  • // 自定义计算流
  • System.out.println("\n------------------------ 自定义计算流 ---------------------");
  • long updateAndGet = atomicLong.updateAndGet(new LongUnaryOperator() {
  • @Override
  • public long applyAsLong(long operand) {
  • return 0x0123456789L;
  • }
  • });
  • System.out.printf("%35s : 0x%016X\n", "updateAndGet(LongUnaryOperator)", updateAndGet);
  • long getAndUpdate = atomicLong.getAndUpdate(new LongUnaryOperator() {
  • @Override
  • public long applyAsLong(long operand) {
  • return 0x0123456789L;
  • }
  • });
  • System.out.printf("%35s : 0x%016X\n", "getAndUpdate(LongUnaryOperator)", getAndUpdate);
  • long getAndAccumulate = atomicLong.getAndAccumulate(0x10, new LongBinaryOperator() {
  • @Override
  • public long applyAsLong(long left, long right) {
  • return left / right;
  • }
  • });
  • System.out.printf("%35s : 0x%016X\n", "getAndAccumulate(0x10, _ / 0x10)", getAndAccumulate);
  • long accumulateAndGet = atomicLong.accumulateAndGet(0x10, new LongBinaryOperator() {
  • @Override
  • public long applyAsLong(long left, long right) {
  • return left / right;
  • }
  • });
  • System.out.printf("%35s : 0x%016X\n", "accumulateAndGet(0x10, _ / 0x10)", accumulateAndGet);
  • // 对比
  • System.out.println("\n------------------------ compareAndSet ------------------");
  • System.out.printf("%35s : 0x%016X\n", "get()", atomicLong.get());
  • System.out.printf("%35s : %s\n", "compareAndSet(0x12345679L)", atomicLong.compareAndSet(0x12345679L, 0xFEDCBA9876543210L));
  • System.out.printf("%35s : 0x%016X\n", "get()", atomicLong.get());
  • System.out.printf("%35s : %s\n", "compareAndSet(0x0000000001234567L)", atomicLong.compareAndSet(0x0000000001234567L, 0xFEDCBA9876543210L));
  • System.out.printf("%35s : 0x%016X\n", "get()", atomicLong.get());
  • }
  • }

运行结果如下:

  • ------------------------ 转换类型 ------------------------
  • get() : 0x0123456789ABCDEF
  • intValue() : 0x0000000089ABCDEF
  • longValue() : 0x0123456789ABCDEF
  • doubleValue() : 8.1985529216486896E16
  • floatValue() : 8.1985531E16
  • ------------------------ +1或-1操作 ----------------------
  • getAndDecrement() : 0x0123456789ABCDEF
  • decrementAndGet() : 0x0123456789ABCDED
  • getAndIncrement() : 0x0123456789ABCDED
  • incrementAndGet() : 0x0123456789ABCDEF
  • ------------------------ +n操作 --------------------------
  • addAndGet(0x10) : 0x0123456789ABCDFF
  • getAndAdd(0x10) : 0x0123456789ABCDFF
  • ------------------------ 自定义计算流 ---------------------
  • updateAndGet(LongUnaryOperator) : 0x0000000123456789
  • getAndUpdate(LongUnaryOperator) : 0x0000000123456789
  • getAndAccumulate(0x10, _ / 0x10) : 0x0000000123456789
  • accumulateAndGet(0x10, _ / 0x10) : 0x0000000001234567
  • ------------------------ compareAndSet ------------------
  • get() : 0x0000000001234567
  • compareAndSet(0x12345679L) : false
  • get() : 0x0000000001234567
  • compareAndSet(0x0000000001234567L) : true
  • get() : 0xFEDCBA9876543210