CAS
悲观锁与乐观锁
- 悲观锁:
我们常说的锁,我们任务访问临界资源总是会发生冲突,所以访问临界资源前要获取到锁,以保证只有一个线程可以访问临界资源。
- 乐观锁:
乐观锁又称无锁,它假设共享资源很少发生冲突,线程不断的执行。发生冲突时使用 CAS 技术来保证线程安全性。
乐观锁 没有使用锁,所以不会发生死锁。但会不断执行线程,产生自旋,消耗CPU资源。
CAS 概念
CAS 全称 Compare And Swap,意为比较并且交换,这时一个原子行为,只有相等的时候才会赋值成功。
当多个线程同时进行CAS操作时,只有一个会成功,其他线程返回失败,其他线程可以继续重试,也可以放弃。
CAS 原理
- CAS 是一个原子操作,Java 如何实现的呢?
Java 中有一个 Unsafe 类,里面有关于 CAS 的native方法。native 方法由 底层的 JVM 使用 C 或 C++ 实现,CAS 实现依赖于 操作系统和CPU,例如 X86 平台依赖于 cmpxchgl
指令实现 CAS 操作。
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
原子操作 AtomicInteger
JDK 提供了用于原子操作的类,位于 java.util.concurrent.atomic
我们以 AtomicInteger
类为例,看看 Java 如何实现原子操作的。
来看 getAndAdd
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
unsafe 底层实现,采用 do-while 循环。保证原子操作。
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;
}
unsafe是初始化静态变量 , valueOffset 是内部 value 字段偏移量。
private static final Unsafe unsafe = Unsafe.getUnsafe();
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;
CAS 原子操作的三大问题
ABA 问题
所谓 ABA 问题,就是一个值原来为 A,更新成了 B,然后又更新为 A。这个时候使用 CAS 时检测不出变化的,实际上数据变化了2次。
解决方法是在变量前面加上版本号或者时间戳。
Java 提供了 AtomicStampedReference
来解决 ABA 问题。
循环时间长开销大
- JVM 底层优化,例如支持 处理器的 pause 指令。
- 锁升级,达到一定条件后,停止 CAS 操作。
只能保证一个共享变量的原子操作
- 使用锁,保证只有一个线程可以操作
- 使用
AtomicReference
类保证对象间的原子性,把多个对象放到一个对象里进行 CA 操作。