我的博客java篇-CAS自旋
概述
CAS
的全称是Compare And Swap
即比较交换,是一种乐观锁机制
- 执行函数:
CAS(V,E,N)
其包含3
个参数- V 表示要更新的变量
- E 表示预期值
- N 表示新值
如何要更新的变量等于预期值,就把新值赋值给变量,如何要更新的变量不等于预期值,就CAS
再重新试一下,再试的时候,会重新读取要更新的变量作为预期值
比方说:
当前的这个线程想改这个值,我期望你是0
,你就不能是1
;如果是1
,那就说明我这个值不对,然后想把你变成1
。大概就是:原来这个值是变为3
了,我这个线程想修改这个值的时候我一定期望你现在是3
,是3
我才改,如果在我修改的过程你变4
了,说明就有另外一个线程修改过该值,那我cas
就再重新试一下,再试的时候,我希望你的这个值是4
,在修改的时候期望值是4
,没有其它线程修改该值,那好我给你改成5
,这样就是cas
操作
cas 是 cpu 指令级别上的支持
当你判断的时候,发现是我的期望值,还没有进行新值设定的时候值又被别的线程改变了怎么办?答案是否定的,cas
是cpu
的原语支持,也就是说cas
是cpu
指令级别上的支持,中间不能被打断,不会造成所谓的数据不一致问题
ABA 问题
假设这样一种场景,当第一个线程执行CAS(V,E,U)
操作,在获取到当前变量V
,准备修改为新值U
前,另外两个线程已连续修改了两次变量V
的值,使得该值又恢复为旧值,这样的话,我们就无法正确判断这个变量是否已被修改过
Java
中解决ABA
问题,我们可以使用以下原子类:
- AtomicStampedReference 类
AtomicStampedReference
原子类是一个带有时间戳的对象引用,在每次修改后,AtomicStampedReference
不仅会设置新值而且还会记录更改的时间。当AtomicStampedReference
设置对象值时,对象值以及时间戳都必须满足期望值才能写入成功,这也就解决了反复读写时,无法预知值是否已被修改的窘境
- 加版本,做任何一个值的修改,修改完加一,后面检查的时候连同版本号一起检查