CAS
什么是CAS
在计算机科学中,比较和交换(Conmpare And Swap)是用于实现多线程同步的原子指令。 它将内存位置的内容与给定值进行比较,只有在相同的情况下,将该内存位置的内容修改为新的给定值。 这是作为单个原子操作完成的。 原子性保证新值基于最新信息计算; 如果该值在同一时间被另一个线程更新,则写入将失败。 操作结果必须说明是否进行替换; 这可以通过一个简单的布尔响应(这个变体通常称为比较和设置),或通过返回从内存位置读取的值来完成(摘自维基本科)
一些记忆点:
1.JDK1.5引入
2.Compare And Swap / 自旋锁 / 轻量级锁 / 乐观锁
3.juc(java.util.concurrent)包下AtomicInteger 等相关类底层都是CAS实现
底层原理
底层依靠Unsafe的CAS操作来保证原子性,从上到下依次查看源码,以AtomicInteger为例:
/**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
Unsafe.class
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));//native方法
return var5;
}
********
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);//底层c++实现
compareAndSwapInt为native方法,对应底层hotspot虚拟机unsage.cpp,以openjdk-8u为例:
代码位置:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/89fb452b3688/src/share/vm/prims/unsafe.cpp
***
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
UnsafeWrapper("Unsafe_CompareAndSwapInt");
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END
***
这里可以看到最终使用了Atomic::cmpxchg来保证原子性,可继续跟进代码
Atomic::cmpxchg针对不同平台有不同的实现方式,这里以Linux-x86为例:
代码位置:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/89fb452b3688/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp
***
// Adding a lock prefix to an instruction on MP machine
#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "
***
inline jint Atomic::cmpxchg (jint exchange_value, volatile jint* dest, jint compare_value) {
int mp = os::is_MP();
__asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)"
: "=a" (exchange_value)
: "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
: "cc", "memory");
return exchange_value;
}
最重要的指令为 LOCK_IF_MP , MP是指多CPU(multi processors),最终意义为多CPU的情况下需要lock,通过lock的方式来保证原子
lock指令的底层怎么保证原子性因为对这些不是太熟悉就不再做讨论
总结:JAVA中我们使用到涉及到CAS操作的底层实现为对应平台虚拟机中的c++代码(lock指令)实现来保证原子性。
CAS缺点
1.因为用自旋的方式来修改变量值,如果高并发情况下会造成循环时间长资源开销大的问题
2.只能保证一个共享变量的原子操作
3.会引发ABA问题
解决办法是在每次修改时增加版本或者时间戳来标记
jdk中可使用 AtomicStampedReference/AtomicMarkableReference
如果是简单值可以不需要版本号,引用类型复杂类型使用版本号来解决ABA问题