定义
- CAS(CompareAndSwap,CompareAndSet)比较并替换,核心逻辑是 先获取当前的值,传入期望值和要修改成的值,如果期望值和现有的值匹配,就将值改成传入的要修改成的值。
- CAS是CPU的一个指令(需要JNI调用Native方法,才能调用CPU的指令),因此保证了原子性操作,不会在比较并替换的时候被其他线程修改
- CAS是非阻塞的、轻量级的乐观锁
作用
- 因为是无锁,非阻塞、轻量级的乐观锁,许多底层使用的都是CAS,例如:java.util.concurrent.atomic的原子类,Unsafe,自旋锁等等…
原理
- 可以通过分析原子类来了解CAS的实现原理。
1.AtomicInteger类
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();//count++
//自增本来在多线程中是要加同步的,这里使用的是unsafe类中的方法
private static final Unsafe unsafe = Unsafe.getUnsafe();
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
复制代码
2.Unsafe类
jdk1.8使用反射获取实例,jdk1.9版本提供一个获取实例的方法,jdk11以后就禁止了
@CallerSensitive //只能由开发者调用,其他无权限
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);//根据当前的对象和偏移量获取相应的值
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
/**
* CAS
* @param o 包含要修改field的对象
* @param offset 对象中某field的偏移量
* @param expected 期望值
* @param update 更新值
* @return true | false
*通过对象和偏移量获取当前的值,再和期望值对比,如果相同就替换为更新值,返回结果
*/
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int update);
复制代码
ABA问题
-
问题:CAS的核心是比较并替换,在比较的时候只是比对值是否相同,如何有这么一种情况:一个线程先将值设置为A,另一个线程获取到当前值为A并作为期望值,于此同时有一个线程将值从A改为B又改为A,这时候将A作为期望值的线程进行比较时发现是相同的从而进行替换,但是并不知道其实这已经不是从前的那个A了。 在基本数据类型没什么问题,如果是引用数据类型就有可能导致异常。
-
解决方式
加标记,使用 AtomicStampedReference
//创建对象的时候设置值和版本号
private final static AtomicStampedReference<String> ar = new AtomicStampedReference<>("A", 1);
//调研CAS时会比对期望的值和版本号,
boolean isCas = ar.compareAndSet(A, B, 1,2)
/**
* @param expectedReference 引用的期望值
* @param newReference 引用的新值
* @param expectStamp标记的期望值
* @param newStamp 标记的新值
* @return {@code true} 如果成功
*/
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END