AtomicLong是JDK5开始提供的,它的做用是:对长整型进行原子操做。相似的原子类还有:AtomicBoolean、AtomicInteger等,它们的实现原理都是同样的。html
在原子类出现以前,要想让一个long类型的数值完成自增操做保持原子性,那么只能经过加synchronized
或者显式锁,这种解决方式不只会让代码编写更加复杂,并且效率也不高。java
原子类的出现,提供了另外一种解决思路来保证操做的原子性,那就是:CAS,关于CAS的详细说明能够看笔者的另外一篇文章:《关于CAS的一点理解和思考》。缓存
对变量加volatile
关键字,保证了有序性和可见性,再经过CAS
来保证操做的原子性,最终就能保证数据的并发安全。安全
UML AtomicLong的结构仍是很简单的,实现了
java.io.Serializable
接口,表示它能够被序列化,继承了java.lang.Number
,表明它是一个数值,能够转换成其余数值类型,如int、float。markdown
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,你也能够手动指定一个初始值。app
// 手动指定一个初始值
public AtomicLong(long initialValue) {
value = initialValue;
}
// 使用long的默认值0
public AtomicLong() {
}
复制代码
AtomicLong提供了两个add方法:addAndGet
和getAndAdd
。ide
addAndGet() 先添加再获取:函数
/* 以原子的方式加上给定值,再返回 */
public final long addAndGet(long delta) {
return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
}
复制代码
getAndAdd() 先添加再获取:oop
/* 以原子的方式加上给定值,返回操做前的结果 */
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的方式尝试去修改,若是修改失败,则说明变量期间已经被其余线程改过了,当前线程会循环重试,直到成功为止。
递增操做,和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;
}
复制代码
递减操做,依然和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;
}
复制代码
比较并设置,以原子的方式,将当前值从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编写,须要调用系统函数。
不多会用到这个方法,并且在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会保证比较并交换
这个过程自己不会被打断。