原子变量操作类
JUC 并发包中包含有 AtomicInteger、AtomicLong 和 AtomicBoolean 等原子性操作类,它们的原理类似,本文讲解 AtomicLong类。Atomicong 是原子性递增或者递减类,其内部使用 Unsafe 来实现,我们看下面的代码。
public class AtomicLong extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1927816293512124184L;
//(1)获取Unsafe实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
//(2)存放变量value的偏移量
private static final long valueOffset;
//(3)判断JVM是否支持Long类型无锁CAS
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
private static native boolean VMSupportsCS8();
static {
try {
//(4)获取value在ATomicLong中的偏移量
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//(5)实际变量值
private volatile long value;
public AtomicLong(long initialValue) {
value = initialValue;
}
}
以上是AtomicLong的源码
代码(1)通过 Unsafe.getUnsafe()方法获取到 Unsafe 类的实例,这里你可能会有疑问,为何能通过 Unsafe.getUnsafe()方法获取到 Unsafe 类的实例?其实这是因为 AtomicLong类也是在 rt.jar 包下面的,AtomicLong 类就是通过 BootStarp 类加载器进行加载的。
代码(5)中的value被声明为volatile的,这是为了在多线程下保证内存可见性,value 是具体存放计数的变量。
代码(2)(4)获取 value 变量在 AtomicLong 类中的偏移量。
下面重点看 AtomicLong 中的主要函数。
1. 递增和递减操作代码
调用unsafe方法,原子性设置value值为原始值+1,返回值为递增后的值
public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
//调用unsafe方法,原子性设置value值为原始值-1,返回值为递减之后的值
public final long decrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
}
//调用unsafe方法,原子性设置value值为原始值+1,返回值为原始值
public final long getAndIncrement() {
return unsafe.getAndAddLong(this, valueOffset, 1L);
}
//调用unsafe方法,原子性设置value值为原始值-1,返回值为原始值
public final long getAndDecrement() {
return unsafe.getAndAddLong(this, valueOffset, -1L);
}
在如上代码内部都是通过调用Unsafe的getAndAddLong方法来实现操作,这个函数是个原子性操作,这里第一个参数是 AtomicLong实例的引用,第二个参数是 value 变量在 AtomicLong 中的偏移值,第三个参数是要设置的第二个变量的值。
public final long getAndAddLong(Object var1, long var2, long var4) {
long var6;
do {
var6 = this.getLongVolatile(var1, var2);
} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6;
}
boolean compareAndSet(long expect, long update )方法
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
由如上代码可知,在内部还是调用了unsafe.compareAndSwapLong方法。如果原子变量中的 value 值等于 expect,则使用 update 值更新该值并返回 true,否则返回 false。
下面通过一个多线程使用 AtomicLong统计0的个数的例子来加深对AtomicLong 的理解。
/*
* 统计0的个数
* */
public class Atomic {
//(10)创建Long型原子计数器
private static AtomicLong atomicLong = new AtomicLong();
//(11)创建数据源
private static Integer[] arrayOne = new Integer[]{0,1,2,3,0,5,6,0,56,0};
private static Integer[] arrayTwo = new Integer[]{10,1,2,3,0,5,6,0,56,0};
public static void main(String[] args) throws InterruptedException {
//(12)创建one统计数组arrayOne中0的个数
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
int size = arrayOne.length;
for(int i = 0; i < size; i++) {
if(arrayOne[i].intValue()==0){
atomicLong.incrementAndGet();
}
}
}
});
//(13)创建two统计数组arrayTwo中0的个数
Thread threadTwo = new Thread(new Runnable() {
@Override
public void run() {
int size = arrayTwo.length;
for(int i = 0; i < size; i++) {
if(arrayTwo[i].intValue()==0){
atomicLong.incrementAndGet();
}
}
}
});
//(14)启动子线程
threadOne.start();
threadTwo.start();
//(15)等待线程执行完毕
threadOne.join();
threadTwo.join();
System.out.println("count 0 的数量为:"+ atomicLong.get());
}
}
如上代码中的两个线程各自统计自己所持数据中0的个数,每当找到一个0就会调用AtomicLong的原子性递增方法。
在没有原子类的情况下,实现计数器需要使用一定的同步措施,比如使用synchronized 关键字等,但是这些都是阻塞算法,对性能有一定损耗,而本文介绍的这些原子操作类都使用CAS非阻塞算法,性能更好。但是在高并发情况下AtomicLong还会存在性能问题。JDK8提供了一个在高并发下性能更好的LongAdder 类,下一篇我们来讲解这个类。