CAS是Compare And Swap。它是java.util.concurrent包的基石。
我们知道,如果线程直接对volatile变量赋值(原子性操作),也是可以实现线程安全的,但更多情况,我们线程对volatile变量的修改与volatile变量的当前值是有关系的!!,这就需要先对这个volatile变量读,然后再写,这就有了至少两个步骤了,所以这个操作不是原子性的,所以就不能保证线程安全。
由此,java使用CAS操作,通过底层实现将变量读-变量写过程变为原子执行!其利用到了缓存锁定,通过将其他各个线程中的该变量的缓存锁定,不允许操作,然后在本线程对该变量进行修改,然后再刷入主内存。
下面以AtomicInteger来研究CAS的具体实现过程。
/**
* AtomicInteger.java
*/
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static{
try{
.....初始化valueOffset.....
}
}
private volatile int value;
public final int get(){
return value;
}
public final int incrementAndGet(){
return unsafe.getAndAddInt(this, valueOffset, 1);
}
/**
* Unsafe.java
* getIntVolatile 方法和 compareAndSwapInt 为本地方法调用
* o 为需要修改的对象
* offset为需要修改字段到对象头的偏移量
* delta为更新增加量
* expected为期望值
* x为更新值
*/
public final int getAndInt(Object o, long offset, int delta){
int v;
do {
v = getIntVolatile(o, offset);
}while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
public native int getIntVolatile(Object o, long offset);
// 该方法即为CAS,它借助C语言调用CPU底层指令实现,大致流程是 判断对象o中要修改的字段的值是否为 expected。当相同时,将x值写入o
public native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
/**
* compareAndSwapInt本地方法的源代码片段
*/
// mp为处理器核心数量
int mp = os::is_MP();
__asm {
mov edx, dest
mov ecx, exchange_value
mov eax, compare_value
// 带有此LOCK前缀的指令在执行期间可以保证执行的原子性(通过锁缓存)、禁止指令重排序、将写缓冲区中所有数据刷新到内存
LOCK_IF_MP(mp)
// cmpxchg 指令 将AL/AX/EAX/RAX寄存器中的值与首操作数比较,如果相等,则将第二操作数的值装载到首操作数 由于edx装的是指针,所以这里首操作数为 dword ptr [edx],表明这该指针指向的单元是一个double word (双字单元), 即4字节。
cmpxchg dword ptr [edx], ecx
}
LOCK前缀有如下说明:
- 确保对内存的读写操作原子执行(在这里使cmpxchg原子执行)
- 禁止该指令(cmpxchg)与之前和之后的读和写指令重排序 (即 使其具有volatile读、写语义)
- 把写缓冲区的所有数据刷到内存中 (上一条的实现,必须具备的能力)
综上所述,可以得出结论:
- CAS操作的底层实现用到了LOCK前缀和cmpxchg指令,它将CAS成为一个原子操作