CAS操作(比较并交换)

265 阅读2分钟

一个问题

思考一个问题,假设有一个变量i=0,两个并发的程序同时对i进行i++的操作,会发生什么事情,i最终的值会是2吗?

在理想的状态下,cpu先运行程序1,对i进行+1操作,然后再运行程序2,最终i变成了2

image.png

但是事实上,i++操作并非一个原子操作,它由三条原子操作组成

image.png

所以在现实情况下,有可能是下面的情况

image.png

左边的情况将会满足程序的预期,但是有可能出现右边的情况,i最终的值为1,就不满足我们程序的预期了

我们是否可以把i++作为一个原子操作呢,可以是可以,但是,如果i++作为了一个原子操作。我希望的是i+=2呢,是不是也要把i+=2作为一个原子操作。

CAS原子操作

人们想出了一个聪明的方法,系统提供一个原子操作CAS(compare and swap 或 Compare And Set)比较并交换,需要传递三个值,数据所在地址,数据过去的值,数据需要更新的值。

cpu会先比较传入数据原来的值是否和内存中当前的值是否相等,如果相等才更新内存中的值,否则操作失败。 对于刚才的i++ 可以使用cas(&i, i, i + 1)替代,那么如果操作失败了呢?那就循环重新再来一次,直到成功

while(! cas(&i,i, i+1));

ABA

CAS会出现的一个经典问题就是ABA了,比如说:

  • 一个线程1获取到内存中a的值为A
  • 然后线程2把a改为了B
  • 然后线程2又把a改为了A
  • 最后线程A再调用CAS,它以为a变量没有被改过。执行成功

解决方法:版本号

  • 对不仅需要传递值,还需要传递版本号,只有值相等且版本号相等才认为该值没有被修改过