CAS

250 阅读2分钟

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比较时,会发现值是一样的。