AtomicLong源码分析

Posted 程序员小潘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtomicLong源码分析相关的知识,希望对你有一定的参考价值。

AtomicLong 是 JDK5 开始提供的,它的作用是:对长整型进行原子操作。类似的原子类还有:AtomicBoolean、AtomicInteger 等,它们的实现原理都是一样的。

在原子类出现之前,要想让一个 long 类型的数值完成自增操作保持原子性,那么只能通过加synchronized或者显式锁,这种解决方式不仅会让代码编写更加复杂,而且效率也不高。

原子类的出现,提供了另一种解决思路来保证操作的原子性,那就是:CAS,关于 CAS 的详细说明可以看笔者的另一篇文章:《关于 CAS 的一点理解和思考》。

对变量加volatile关键字,保证了有序性和可见性,再通过CAS来保证操作的原子性,最终就能保证数据的并发安全。

UMLAtomicLong 的结构还是很简单的,实现了java.io.Serializable接口,表示它可以被序列化,继承了java.lang.Number,代表它是一个数值,可以转换成其他数值类型,如 int、float。

属性

AtomicLong 的属性并不多,它依赖于 Unsafe 类的compareAndSwapLong()方法,只有 Unsafe 才可以调用底层的 CAS 操作。它记录了底层 JVM 是否支持无锁的方式去更新 long 类型,double 和 long 这两个类型比较特殊,占用 64 位空间,具体细节后面可以单独写一篇文章记录下来。AtomicLong 用value代表它的具体数值,被volatile修饰,保证了它的有序性和可见性。

// 需要依赖于Unsafe.compareAndSwapLong()来原子的更新value
private static final Unsafe unsafe = Unsafe.getUnsafe();
// value属性相较于AtomicLong的内存地址偏移量,CAS操作时需要用到
private static final long valueOffset;

/*
记录底层JVM是否支持无锁的方式去更新long类型。
因为long和其他数值有点不一样,它占用8字节,需要占用两个32位的空间,存在写高低位的问题。
 */

static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

/*
记录底层JVM是否支持无锁的方式去更新long类型,并把它缓存在VM_SUPPORTS_LONG_CAS中。
 */

private static native boolean VMSupportsCS8();

// 结果值,volatile保证了它的有序性和可见性。
private volatile long value;

构造函数

AtomicLong 提供了两个构造函数,默认的 value 值为 0,你也可以手动指定一个初始值。

// 手动指定一个初始值
public AtomicLong(long initialValue) {
 value = initialValue;
}

// 使用long的默认值0
public AtomicLong() {
}

核心操作

add

AtomicLong 提供了两个 add 方法:addAndGetgetAndAdd

addAndGet()先添加再获取:

/*
以原子的方式加上给定值,再返回
 */

public final long addAndGet(long delta) {
 return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
}

getAndAdd()先添加再获取:

/*
以原子的方式加上给定值,返回操作前的结果
 */

public final long getAndAdd(long delta) {
 return unsafe.getAndAddLong(this, valueOffset, delta);
}

两个方法的目的都是将 value 加上给定的值,无非就是一个返回操作前的值,一个返回操作后的值。

它们调用的方法都是unsafe.getAndAddLong()方法,这才是核心:

/*
以原子的方式,加上给定值,并返回旧值。
var1:对象实例
var2:value相较于类的内存地址偏移量
var4:加上给定值
*/

public final long getAndAddLong(Object var1, long var2, long var4) {
    long var6;
    do {
     // 从主存中,读取最新值
        var6 = this.getLongVolatile(var1, var2);
        // CAS的方式将值从var6修改为(var6 + var4),如果失败就循环重试,直到成功为止。
    } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
 // 返回旧值
    return var6;
}

实现思路是:从主存中读取变量最新的值,通过 CAS 的方式尝试去修改,如果修改失败,则说明变量期间已经被其他线程改过了,当前线程会循环重试,直到成功为止。

increment

递增操作,和add一样,只是 add 的值为 1。也提供了两个方法:getAndIncrementincrementAndGet,作用都是值递增,一个返回旧值,一个返回新值。

/*
以原子的方式,将value递增,返回旧值。
 */

public final long getAndIncrement() {
 // 和add()一样,递增就是+1
 return unsafe.getAndAddLong(this, valueOffset, 1L);
}

/*
以原子的方式,将value递增,返回新值。
 */

public final long incrementAndGet() {
 return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}

decrement

递减操作,依然和add一样,只是 add 的值为-1。也提供了两个方法:getAndDecrementdecrementAndGet,作用都是值递减,一个返回旧值,一个返回新值。

/**
以原子的方式,将value递减,返回旧值。
 */

public final long getAndDecrement() {
 // 和add()一样,递增就是-1
 return unsafe.getAndAddLong(this, valueOffset, -1L);
}

/**
以原子的方式,将value递减,返回新值。
 */

public final long decrementAndGet() {
 return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
}

compareAndSet

比较并设置,以原子的方式,将当前值从 expect 修改为 update,成功返回 true,失败返回 false,和 CAS 一个道理。

/**
以原子的方式,将当前值从expect改为update
expect:预期的旧值
update:要修改的新值
 */

public final boolean compareAndSet(long expect, long update) {
 return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}

还是依赖于unsafe实现的,unsafe.compareAndSwapLong()就是去调用底层的 CAS 操作,native 方法,使用 C 编写,需要调用系统函数。

weakCompareAndSet

很少会用到这个方法,而且在 JDK8 中,它和compareAndSet代码一模一样,没有任何区别,直到 JDK9 才被实现。它的作用是:操作只保留volatile自身的特性,去除happens-before规则带来的内存语义,即无法保证没有别volatile修饰的其他变量的有序性和可见性。

/**
 * Atomically sets the value to the given updated value
 * if the current value {@code ==} the expected value.
 *
 * <p><a href="package-summary.html#weakCompareAndSet">May fail
 * spuriously and does not provide ordering guarantees</a>, so is
 * only rarely an appropriate alternative to {@code compareAndSet}.
 *
 * @param expect the expected value
 * @param update the new value
 * @return {@code true} if successful
 */

// JDK8中,和compareAndSet一模一样
public final boolean weakCompareAndSet(long expect, long update) {
 return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}

总结

原子类的代码并不复杂,逻辑都很简单,就是通过volatile+CAS的方式来保证数据的并发安全。数值的更新几乎都是依赖于Unsafe类去完成的,CAS 操作本身 Java 代码不能实现,需要调用本地方法,通过 C 去调用系统函数。同时 CAS 本身依赖于现代 CPU 支持的并发原语,即 CPU 会保证比较并交换这个过程本身不会被打断。


以上是关于AtomicLong源码分析的主要内容,如果未能解决你的问题,请参考以下文章

LongAdder核心源码分析

死磕 java并发包之LongAdder源码分析

死磕 java并发包之LongAdder源码分析

Java多线程系列--“JUC原子类”03之 AtomicLong原子类

Java并发编程之LongAdder和LongAccumulator源码探究

Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段