int CompareAndSwap(int* address, int expected, int new) {
    if (*address == expected) { // 如果旧值和理想值一样
        *address = new;         // 更新旧值
        return 1;   // success
    } else {
        return 0;   // failure
    }
}

CAS包含三个参数address, expected, new,其中address表示要更新的变量,expected表示预期值,new表示新值。

仅当address = expected时,才会设置address 的值为new.

如果address 和expected的值不同,说明已经有了其它线程做了更新,则当前线程什么也不做。最后,CAS会返回当前address 的真实值,也可以返回0表示失败。

CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功的完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。(意味着自旋)

所以这也带来下面3个经典问题:

1、自旋CAS长时间不成功,占用CPU浪费资源

解决方案:破环死循环,当自选超过一定时间/次数时,return退出

2、当对于一个共享变量操作,可以使用CAS保证原子操作。但对于多个共享变量,无法保证

解决方案:

  • 还是老实用锁
  • 把多个共享变量合并成一个共享变量,可以一个使用高几位,一个使用低几位
  • 多变量封装成一个对象。java的AtomicReference支持这个操作,c++不知道支不支持

3、ABA问题

CAS在操作值的时候检查值有没有发生变化,没有变化则更新。

如果一个值连续变化两次,原本为A,然后A→B→A,那么此时CAS检查时发现值没有变化,还会进行操作,但是实际上确实变化了。

解决方案:

  • 使用版本号,增加版本号比较
  • java中AtomicStampedReference,首先检查当前引用是否等于预期引用,并且检查当前的标志是否等于预期标志,如果全部相等,则以原子方式将该应用和该标志的值设置为给定的更新值。也就是检查引用而非检查值。