[Java] 简单分析AtomicInteger中的addAndget方法
简单使用AtomicInteger
首先贴出以下的代码,简单的使用AtomicInteger这个类来实现+1的操作。
import java.util.concurrent.atomic.AtomicInteger;
public class CASTest {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(10);
for (int i = 0; i < 10; i ++) {
new Thread(new Runnable() {
@Override
public void run() {
atomicInteger.addAndGet(1);
}
}).start();
}
System.out.println(atomicInteger);
}
}
打印的值是20,可见是线程安全的。以下逐步分析其中蕴含的CAS原理。
提供增加操作的addAndGet方法
分析其中的addAndGet方法,其源码如下
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
发现它是调用unsafe包的方法,首先对传给unsafe的getAnddAddInt的参数进行说明:
- this指当前的AtomicInteger对象
- valueOffset由AtmoicInteger类中的静态代码块确定,指的是AtomicInteger的value属性在内存中的偏移量
- delta即要value值要改变的大小
实际执行修改操作的getAndAddInt方法
再来看unsafe包中的getAndAddInt的方法,为cas原理的核心方法
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
这个方法返回的值其实是修改前的值,所以addAndGet方法返回的是改方法的返回值 + delta
主要有以下步骤:
- do循环中通过getIntVolatile本地方法来获取主存中的Atomic的value值,并赋值给局部变量v
- compareAndSwapInt来同样通过o,offset参数来获取主存中的value值,代码中并未体现
- 通过比较v与compareAndSwapInt方法中再次获得的值来判断修改是否执行,即
v = v + delta
操作 - 成功则跳出循环,返回修改前的值
- 不成功则一直循环,自旋在此处
其他方法
其中getIntVolatile与compareAndSwapInt方法签名如下
//** Volatile version of {@link #getInt(Object, long)}
public native int getIntVolatile(Object o, long offset);
//Atomically update Java variable to x if it is currently holding expected.Returns:true if successful
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
CAS的缺点
可以看到CAS原理其中是不断比较最初的值,和要进行修改时的值进行比较,来判断是否有其他线程修改了此变量;如果变量被修改,则不会继续修改变量。但是CAS是一种自旋的操作,会占用CPU的时间块分配的。以及存在这样一个逻辑漏洞:如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?如果在这段期间它的值曾经被改成了B,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个漏洞称为CAS操作的"ABA"问题。