CAS操作需要三个参数: CAS(V,E,N)
V: 内存值
E:旧的预期值,一个线程间共享的变量,首先主存中会保留一份,然后每个线程的工作内存中也会保留一份副本,即预期值。
N:新值
只有当 V=E的时候,才会将V设为N。
CAS操作是原子性的,所以多线程并发使用CAS更新数据时,可以不使用锁。JDK中大量使用了CAS来更新数据而防止加锁(synchronized 重量级锁)来保持原子更新。
java.util.concurrent包都中的实现类都是基于volatile和CAS来实现的。尤其java.util.concurrent.atomic包下的原子类。
简单介绍下volatile特性:
1. 内存可见性(当一个线程修改volatile变量的值时,另一个线程就可以实时看到此变量的更新值)
2. 禁止指令重排(volatile变量之前的变量执行先于volatile变量执行,volatile之后的变量执行在volatile变量之后)
可以把AtomicInteger看作是一个整数,它是可变的,并且是线程安全的。对其进行修改等任何操作,都是用CAS指令进行的。
内部实现:
核心字段:
private volatile int value;
value字段在AtomicInteger对象中的偏移量:
private static final long valueOffset;
我们先看下 incrementAndGet方法的内部实现:
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
// 1、this:当前的实例 2、valueOffset:value实例变量的偏移量 3、当前value要加上的数(value+delta)。
public class AtomicInteger extends Number implements java.io.Serializable {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
//用于获取value字段相对当前对象的“起始地址”的偏移量
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
//返回当前值
public final int get() {
return value;
}
//递增加detla
public final int getAndAdd(int delta) {
//三个参数,1、当前的实例 2、value实例变量的偏移量 3、当前value要加上的数(value+delta)。
return unsafe.getAndAddInt(this, valueOffset, delta);
}
//递增加1
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
...
}
volatile保证线程的可见性,多线程并发时,一个线程修改数据,可以保证其它线程立马看到修改后的值
CAS 保证数据更新的原子性。
Unsafe(反编译出来的):
public final int getAndAddInt(Object paramObject, long paramLong, int paramInt)
{
int i;
do
i = getIntVolatile(paramObject, paramLong);
while (!compareAndSwapInt(paramObject, paramLong, i, i + paramInt));
return i;
}
public final long getAndAddLong(Object paramObject, long paramLong1, long paramLong2)
{
long l;
do
l = getLongVolatile(paramObject, paramLong1);
while (!compareAndSwapLong(paramObject, paramLong1, l, l + paramLong2));
return l;
} ........
从源码中发现,内部使用自旋的方式进行CAS更新(while循环进行CAS更新,如果更新失败,则循环再次重试)。