cas机制学习[已迁移]
转自:https://www.jianjunwen.com/article/71
1.介绍
CAS是英文单词Compare and Swap的缩写,翻译过来就是比较并替换。
CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。
更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
当V!=A时说明有其他线程抢先修改,会放弃当前操作,之后再重新查询V,重新尝试的过程被称为自旋(Spin)。
//上述链接中有非常清晰的例子和图,有助于理解。
2.缺点
2.1 CPU开销过大
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。(应该是自旋尝试的过程仍然占用cpu时间。)
2.2 不能保证代码块的原子性
https://segmentfault.com/a/1190000016611415
当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i = 2,j = a
,合并一下ij = 2a
,然后用CAS
来操作ij。
2.3 ABA问题
第一个链接中提供了带图的例子,非常容易理解。
简单来说,就是有三个线程,编号为1、2、3,1和2同时查询获取当前值为A,都要修改为B,3查询值为B,要修改为A,执行顺序如下:
- 1操作成功了,将A修改为B,但2阻塞住了;
- 此时3查询值为B,成功将B修改为A;
- 2能运行了,查询当前值为A符合条件,修改为B。
假设1和2是由硬件问题导致的错误重复的取钱操作,那么1和2会被执行两次,会出现不一致的问题。
解决办法:
添加版本号。
真正要做到严谨的CAS机制,在compare阶段不仅要比较期望值A和地址V中的实际值,还要比较变量的版本号是否一致。
那么当1和2在查询时,获取到A值和版本号1,当1修改完后,值为B,版本号为2,当3修改完后值为A,但版本号为3,在2开始执行时判断值虽然一致,但版本号不一致,所以更新失败。