Compare And Swap:比较和交换
v(内存位置)
a(旧的期望值)
b(改变之后的值)
数据在修改之前,旧的预期值和v中的值一致时,才能修改
java中Atomic相关类就是使用的这种算法实现的。
它和Synchronized相对:
Synchoronized属于悲观锁,认为并发数据操作是一个大概率事件(总有刁民想害朕),我操作的时候,得把数据锁上,别人谁也别想动
而CAS属于乐观锁,它认为并发数据操作是小概率事件,你操作数据之后我再核查一遍,现在的数据和我修改的时候,读取的数据是否一致就没事了,通过比较和交换的方式进行数据修改。
拿 AtomicInteger 的 incrementAndGet 方法举例,这个方法就是给当前对象的数值+1,但是保证了线程安全:
public void test(){
AtomicInteger atomicInteger = new AtomicInteger(1);
atomicInteger.incrementAndGet ();
Integer integer = 1;
integer = integer + 1;
// 以上两种操作,结果是一样的
}
看看CAS再JDK1.8中是怎么实现的:
public final int incrementAndGet() {
// getAndAddInt(this, VALUE, 1) 返回之后进行的 +1 操作
return U.getAndAddInt(this, VALUE, 1) + 1;
}
@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
// 不停的去判断,现在的值 和 当时修改时读取的值是否一致
do {
// 获取当前变量的值
v = getIntVolatile(o, offset);
// offset:内存地址的值
// v:旧的期望值
// v + delta :进行内存的数据更新
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
// 返回的却是更新前的数值
return v;
}
// 当前内存存放的值 和 旧的期望值 对比的方法
@HotSpotIntrinsicCandidate
public final boolean weakCompareAndSetInt(Object o, long offset,
int expected,
int x) {
return compareAndSetInt(o, offset, expected, x);
}
@HotSpotIntrinsicCandidate
public final native boolean compareAndSetInt(Object o, long offset,
int expected,
int x)
// 从内存中获取此对象的值
@HotSpotIntrinsicCandidate
public native int getIntVolatile(Object o, long offset);
看到最后,殊途同归,最后都看到了native这个关键字,进入c的部分,而comparenAndSetInt 通过查阅资源,底层实现的方式大致是:
判断当前系统是否为多核处理器;
执行 CPU 指令 cmpxchg,如果为多核则在 cmpxchg 加 lock 前缀。
可以清晰的看出,java中的cas是通过底层cpu命令实现对比和交换,通过cpu实现无锁自增。
优点:
1、它属于cpu指令级操作,速度很快
2、没有线程阻塞带来的性能消耗问题
缺点:
1、无法通过它进行并发控制
2、ABA问题:判断的时候,虽然当前值和预期值一致,但是可能是由于其他线程改修数据之后又修改回来,这个时候就不严谨了(这已经不是当初的那个它了)解决此类问题,可以通过添加一个时间戳来解决。
有什么不足欢迎大家指出和交流!