Java并发编程 --- CAS与原子变量
书接上回,我们谈到了Unsafe类,那就不得不谈谈CAS了。
CAS
概述
CAS全称为Compare-And-Swap,对比交换。它是一条CPU原子指令,作用在于让CPU比较两个值是否相等,然后更新某个值。
CAS是靠硬件实现的。CAS操作是原子性的,所以多线程并发使用CAS更新数据时,可以不使用锁。
实现保证-Unsafe
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object update);
public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);
CAS操作的参数 → 内存位置、预期原值及新值。
执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。
核心原理 - cmpxchg指令
UnSafe类中调用的CAS实际执行的JNI本地方法,该JNI方法本质上是调用cmpxchg指令。
指令格式: dest 目标操作数 src 修改操作数
带前缀lock保证原子性
lock cmpxchg dest,src
cmpxchg的原子性保证
1、当执行cmpxchg会通过锁总线和缓存锁定来保障原子性。
锁总线:指令执行期间锁定内存总线,阻止其他CPU核心访问内存。
缓存锁定:如果目标内存地址在缓存中处于独占状态(MESI协议),则直接锁缓存,无需总线锁。
cmpxchg的可见性保证
1、缓存一致协议(MESI协议)
现代 CPU 通过 MESI协议维护多核缓存一致性。
当cmpxchg修改内存值后,其他 CPU 核心会通过该协议感知到缓存行的失效,强制从主存或上级缓存重新加载最新值。
2、隐式内存屏障
cmpxchg 指令本身包含 隐式内存屏障(如 lock 前缀),确保指令执行前后的读写操作顺序不被重排序,且修改后的值立即对其他线程可见。
问题
ABA问题
如果只是检查值的话,如果我们需要把一个值从A→C,那如果在这期间有其他线程将A→B→A,那么CAS检查之后发现还是为A,所以会成功更新为C。
解决思路:使用版本号
竞争激烈的话开销大对CPU不友好
如果存在大量竞争,那么会导致很多线程一直在自旋,那么对CPU来说比较不友好。
解决思路:如果预估资源竞争比较激烈,那么使用悲观锁会比较好。
只能保证一个共享变量的原子操作
当对一个共享变量执行操作时,我们可以保证它的原子操作。但当超过一个时,就无法保证。
原子变量
AtomicStampedReference
为什么先谈它呢,因为它解决了ABA问题。
解决思路:引入版本号
public class AtomicStampedReference<V> {
private static class Pair<T> {
final T reference; //维护对象引用
final int stamp; //用于标志版本
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
private volatile Pair<V> pair;
/**
* expectedReference :更新之前的原始值
* newReference : 将要更新的新值
* expectedStamp : 期待更新的标志版本
* newStamp : 将要更新的标志版本
*/
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) ||
// 构造新的Pair对象并CAS更新
casPair(current, Pair.of(newReference, newStamp)));
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
// 调用Unsafe的compareAndSwapObject()方法CAS更新pair的引用为新引用
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
AtomicInteger
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
//用来获取value字段相对于当前对象的"起始地址"偏移量
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//volatile保证了各线程对该值的可见性
private volatile int value;
//返回当前的值
public final int get() {
return value;
}
//先加后再返回值
public final int addAndGet(int delta) {
return U.getAndAddInt(this, VALUE, delta) + delta;
}
//进行加法 但是只需要返回加前的值
public final int getAndAdd(int delta) {
return U.getAndAddInt(this, VALUE, delta);
}
@IntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
//getIntVolatile 本地方法:通过偏移量获取当前的内存值
v = getIntVolatile(o, offset);
} while (!weakCompareAndSetInt(o, offset, v, v + delta));//循环 乐观锁重试机制
return v;
}
@IntrinsicCandidate
public final boolean weakCompareAndSetInt(Object o, long offset,
int expected,
int x) {
return compareAndSetInt(o, offset, expected, x);
}
//本地方法 判断并替换
@IntrinsicCandidate
public final native boolean compareAndSetInt(Object o, long offset,
int expected,
int x);

浙公网安备 33010602011771号