CAS是什么
CAS(Compare And Swap)比较与替换。在高并发编程中大量使用。
为什么需要CAS
在多线程环境下对一个变量进行并发修改是不能保证原子性的。例如多线程下对一个int类型变量进行++操作,线程A读取int i = 0; 对 i 进行++操作,线程B在线程A修改更新 i 之前去读取 i = 0; 也进行++ 操作;期待的结果为2,但最终结果却是1。有人可能会问这不是可见行问题么?添加 volatile修饰能不能解决,答案是不能。线程B在读取 i 时,线程A还没有进行修改更新(并发执行)。
对于变量自增的并发解决方法AtomicInteger就是使用volatile+CAS来保证多线程下变量的原子性的。
CAS实现原理
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。内存位置的值V与预期值比较,匹配更新为B是底层同步的。
CAS 的缺点
- ABA问题,如果有一个变量A,线程1修改变量A为B,线程2又把变量B改回了A,线程3预期变量值为A才会把值改为C,线程3以为变量的值没有变化,但是它却被修改过。解决方案,添加一个版本号,每次对变量进行修改的时候对版本号+1,比较的时候看下版本号是否一致。
- CAS无锁自旋修改变量,如果一个变量一直修改不成功,很消耗cpu资源。
CAS在AtomicInteger中的应用
AtomicInteger 可以对变量进行线程安全的原子的增减操作。
// 底层维护保存的变量,使用volatile保证内存可见
private volatile int value;
// 保存value在类信息中的偏移量
private static final long valueOffset;
static {
try {
// 使用unsafe类初始化 value 的偏移量
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
getAndIncrement 方法
public final int getAndIncrement() {
// 还如当前对象,value偏移量,增加数量
return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
// 自旋一直尝试修改
do {
// 拿到当前的value值
var5 = this.getIntVolatile(var1, var2);
// CAS进行比较和替换
// 参数(当前对象,value地址偏移量,预期值A,新值B)
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
// 修改成功后返回
return var5;
}
Unsafe类的作用于使用
参考文章
交流Q群 892480622