什么是CAS
CAS是Compare And Swap即比较并交换的缩写。
CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。
CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作(因为调用了CPU提供的指令,intel CPU中为cmpxchg)。
CAS一般是配合无限循环使用的。
CAS的三个问题
- ABA问题
- 循环时间开销很大
- 只能保证一个变量的原子操作
ABA问题
因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
解决方法:使用版本号
在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A - 2B-3A。
Java并发包为了解决这个问题,提供了一个带有标记的原子引用类“AtomicStampedReference” ,它可以通过控制变量值的版本来保证CAS的正确性。因此,在使用CAS前要考虑清楚“ABA”问题是否会影响程序并发的正确性,如果需要解决ABA问题,改用传统的互斥同步可能会比原子类更高效。
循环开销很大
CAS 出现冲突后就会开始自旋操作,如果资源竞争非常激烈,自旋长时间不能成功就会给 CPU 带来非常大的开销。
解决方法:限制循环次数;延迟执行。
只能保证一个变量的原子操作
当对一个变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个变量操作时,CAS 目前无法直接保证操作的原子性。
解决方法:使用互斥锁来保证操作原子性;将多个变量操作合成一个变量操作。从 JDK1.5 开始提供了AtomicReference 类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。