AtomicLong源码导读

1,491 阅读5分钟

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

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

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

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

UML 在这里插入图片描述 AtomicLong的结构还是很简单的,实现了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会保证比较并交换这个过程本身不会被打断。