这篇 https://juejin.im/post/5ae753d8f265da0ba56753ca 博客中,已经对CAS做了很详细的解释,这篇文章目的是整理一下思路。
写在开篇
CAS(compareAndSwap,比较交换,一种无锁原子算法)过程是这样:它包含 3 个参数 CAS(V,E,N),V表示要更新变量的值,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做两个更新,则当前线程则什么都不做。最后,CAS 返回当前V的真实值。CAS 操作时抱着乐观的态度进行的,它总是认为自己可以成功完成操作。
非阻塞算法
一个线程失败或挂起,不应该影响其他线程失败或挂起算法。
底层原理
归功硬件指令集的发展,硬件保证一个从语义上需要多次操作只通过一条指令就能完成。
Java如何实现原子操作
Java提供了java.util.concurrent.atomic包。
我们可以通过AtomicInteger的compareAndSet简单看一下其内部实现。1
2
3public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
该方法调用了unsafe类(本地方法)的compareAndSwapInt方法,有几个参数,一个是对象自身,一个是该变量的内存地址,一个是期望值,一个是更新值。完全符合我们之前CAS的定义。
缺点
CAS存在ABA问题:假设一个变量A,修改为B之后又修改为A,CAS的机制是无法察觉的,但实际上已经被修改过了。如果在基本类型上是没有问题的,但是如果是引用类型呢?这个对象中有多个变量,我们怎么知道有没有被改过?
聪明的你一定想到了,加个版本号啊。每次修改就检查版本号,如果版本号变了,说明改过,就算你还是A,也不行。
在java.util.concurrent.atomic包中,通过tomicReference来保证引用的原子性。