一、什么是CAS?
CAS(Compare And Swap),就是比较并交换,是解决多线程情况下,解决使用锁造成性能损耗问题的一种机制。
CAS包含三个操作数:
- 变量内存位置(V)
- 预期的变量原值(A)
- 变量的新值(B)
当要对变量进行修改时,先会将内存位置的值与预期的变量原值进行比较,如果一致则将内存位置更新为新值,否则不做操作,无论哪种情况都会返回内存位置当前的值。
二、CAS的实践案例
整个JUC都是建立在CAS之上的,我们以Java8为例,看看AtomicInteger
的CAS实现:
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
/**
* Unsafe类是提供了native方法,用于完成硬件级别的原子性操作
* 在这里提供Unsafe.compareAndSwapInt方法来完成CAS的比较并更新
*/
private static final Unsafe unsafe = Unsafe.getUnsafe();
/**
* value在内存中的偏移量,是即CAS中的内存地址V
*/
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
// 省略一大串代码
/**
* 通过unsafe类提供的native方法完成CAS操作
*
* @param expect 预期的变量原值(A)
* @param update 变量的新值(B)
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* 进行CAS操作,自旋直到数据更新成功
*
* @param updateFunction a side-effect-free function
* @return 更新后的值
* @since 1.8
*/
public final int updateAndGet(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return next;
}
}
compareAndSet的CAS实现:通过Unsafe的方法,调用native方法完成了硬件级别的原子性操作。
注意,Unsafe方法可能会在未来的jdk版本中移除,而且使用一旦出现问题可能就是JVM实例崩溃级别的问题,所以官方不推荐在应用代码中使用。
三、CAS存在的问题
ABA问题:CAS在检查值的时候,只会比较预期值A与内存位置的值是否相同,如果内存位置值,经过若干次修改又变回了A ( A -> B -> A),CAS检查依旧会通过,但是实际上这个值已经修改过了。
解决方案:解决的思路就是引入类似乐观锁的版本号控制,不止比较预期值和内存位置的值,还要比较版本号是否正确。
实现案例:从JDK5开始,atomic包就提供了AtomicStampedReference
类来解决ABA问题,相较CAS引入了一个标志,在比较完预期值与内存地址值之后,再对预期标志和现有标志做比较,都通过才执行更新操作。