Java CAS算法
前言:
在之前的Java并发关键字volatile详解一文中,得出结论,对于像 i++这样的复合操作,volatile是不能保证其原子性,而为了保证原子性,在java.util.concurrent.atomic包下面提供了很多原子型的方法和数据类型。比如我们常用的AtomicInteger,AtomicBoolean,AtomicLong等类型的数据,这些都能保证原子性。那么问题来了,他们是如何做到保证原子性的呢?这就涉及到了CAS算法,也就是本文的主题。
CAS算法:CompareAndSwap
-
概述:
CAS翻译过来就是:比较并且交换。而怎么交换呢?如下详解:
-
详解:
如图所示,在主内存中有一个值V=0,线程1读取该值以后,存入自己的内存中,也就是exceptVal,这个值是主内存值V的一个副本,也叫期望值。当线程1里的方法对读取到的值进行操作(不会把exceptVal修改,而是重新复制一份去操作),比如修改为1,此时有一个新值,假定为X=1,也就是说把exceptVal的副本修改为1。
然后把这个修改过的值重新写回到主内存,此时就需要把exceptVal和V进行比较,如果两个一样,那说明原来的值没有被修改过(暂时忽略ABA问题),然后进行回写。如果两个值不一样,那说明主内存里面的值被修改过了,则执行自旋。
自旋代码:(仅仅是其中一种情况)
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)); return var5; }自旋的大概流程就是,当发现exceptVal和V不一样时,那就说明V被其他线程修改了,然后去主内存里再读取一次V,重复执行上一次对V的操作流程,执行完再进行比较,如果两个值一样,则回写,不一样再次重复上述操作,直到exceptVal和V一样。
-
ABA问题:
在上面的CAS算法中,存在一个问题,当线程1读取完主内存的数据V以后,如果线程2把V修改成了1,假设还有一个线程3,又把V修改回了0,当线程1进行exceptVal和V比较时,会发现值是一样的。