AtomicInteger源码分析

在阅读ConcurrentHashMap源码的过程中,频繁使用了volatile+无锁的方式来处理并发,需要对AtomicInteger源码有更深的了解。将这个模块独立抽离出来形成更深刻的理解。

AutomicInteger变量

AutomicInteger作为一个原子操作类,定义了一些变量:

  • private static final Unsafe unsafe = Unsafe.getUnsafe(),定义了一个usafe类对象,主要对native方法进行调用。

  • private static final long valueOffset,这个值可能会被误以为是默认值,查看部分源代码:

    1
    2
    3
    4
    5
    6
    static {
    try {
    valueOffset = unsafe.objectFieldOffset
    (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
    }

    了解过java虚拟机类加载机制的,类加载触发的其中一个条件是getstatic,这样其他方法在每次访问到这个值的时候都会执行一下静态代码块获取到内存中最新的值。这个值是作为内存中的值,我们在编写程序的时候会存一份拷贝在栈中,通过对比这个值和拷贝值是否一致来决定是否更新新值到内存,也就是这次操作是否成功。

  • private volatile int value,上面通过静态代码块儿去加载的就是内存中value的最新值。

AutomicInteger原子操作

这里我们通过自己写复杂操作count++对比通过调用AutomicInteger的api,可以对比结果是否保证了正确的输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package CasNoLock;



import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;

/**
* Created by yqz on 3/16/18.
*/
public class Counter {
private static volatile int count;
private static AtomicInteger atomicInteger=new AtomicInteger(0);
public static void main(String[] args) {
ArrayList<Thread> threadArrayList=new ArrayList<Thread>(100);
for(int i=0;i<100;i++){
Thread thread=new Thread(new Runnable() {
public void run() {
for(int i=0;i<5000;i++){
count();
countAtomicInteger();
}
}
});
threadArrayList.add(thread);
}
for(Thread t:threadArrayList){
t.start();
}
for(Thread t:threadArrayList){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("非原子操作累加结果:"+count);
System.out.println("原子操作累加结果:"+atomicInteger.intValue());
}

/**
* 非原子类操作
*/
private static void count(){
count++;
}
/**
* 原子类操作
*/
private static void countAtomicInteger(){
atomicInteger.getAndIncrement();
}
}

输出:

1
2
非原子操作累加结果:461539
原子操作累加结果:50000

AutomicInteger API分析

我们查看下atomicInteger.getAndIncrement()在usafe中的实现,实际上它是调用了

1
2
3
4
5
6
7
8
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}

查看getAndAddInt方法

1
2
3
4
5
6
7
8
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

return var5;
}

compareAndSwapInt(var1, var2, var5, var5 + var4)换成compareAndSwapInt(obj, offset, expect, update)比较清楚,意思就是如果obj内的valueexpect相等,就证明没有其他线程改变过这个变量,那么就更新它为update,如果这一步的CAS没有成功,那就采用自旋的方式继续进行CAS操作,取出乍一看这也是两个步骤了啊,其实在JNI里是借助于一个CPU指令完成的。所以还是原子操作。

总结

AutomicInteger源码最终是通过usafe.compareAndSwapInt(obj, offset, expect, update)函数实现cas原子操作的。cas中最重要的三个值,volatile value(内存中最新值),expect(拷贝值)和update(更新值),在函数中分别对应offset,expect,和update入参。对这个关键方法的了解可以帮助深入阅读ConcurrentHashMap源码,jdk1.8中大量运用cas+synchronized关键字来实现。