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 方法:addAndGet
和getAndAdd
。
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。也提供了两个方法:getAndIncrement
和incrementAndGet
,作用都是值递增,一个返回旧值,一个返回新值。
/*
以原子的方式,将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。也提供了两个方法:getAndDecrement
和decrementAndGet
,作用都是值递减,一个返回旧值,一个返回新值。
/**
以原子的方式,将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源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Java多线程系列--“JUC原子类”03之 AtomicLong原子类
Java并发编程之LongAdder和LongAccumulator源码探究
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段