CAS 操作包含三个操作数:
-
内存位置(V):需要更新的变量。
-
期望值(A):认为变量当前的值。
-
新值(B):希望将变量更新为的值。
CAS 的操作逻辑是:
-
比较内存位置的值是否等于期望值(A)。
-
如果相等,则将内存位置的值更新为新值(B)。
-
如果不相等,则不做任何操作。
CAS 的操作是原子的,即在执行过程中不会被其他线程打断。
CAS 的伪代码
boolean compareAndSwap(int memory, int expected, int newValue) {
if (memory == expected) {
memory = newValue;
return true;
} else {
return false;
}
}
CAS 的应用示例
实现自旋锁
public final boolean compareAndSet(boolean expect, boolean update)
class SpinLock {
private AtomicBoolean locked = new AtomicBoolean(false);
public void lock() {
while (!locked.compareAndSet(false, true)) {
// 自旋等待
}
}
public void unlock() {
locked.set(false);
}
}
实现计数器
class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
int oldValue;
int newValue;
do {
oldValue = count.get();
newValue = oldValue + 1;
} while (!count.compareAndSet(oldValue, newValue));
}
public int getCount() {
return count.get();
}
}
CAS 的优缺点
优点
-
无锁:避免了线程阻塞和上下文切换的开销。
-
高性能:在低竞争场景下,性能优于传统的锁机制。
-
简单易用:提供了丰富的原子操作方法。
缺点
-
ABA 问题:CAS 操作无法感知到值从 A 变为 B 又变回 A 的情况。
-
高竞争场景性能下降:在高竞争场景下,CAS 操作可能会频繁失败,导致性能下降。
-
只能保证一个变量的原子性:CAS 操作只能保证一个变量的原子性,无法保证多个变量的原子性。
CAS 的 ABA 问题
ABA 问题的描述
ABA 问题是指:
-
线程 1 读取变量的值为 A。
-
线程 2 将变量的值从 A 改为 B,然后又改回 A。
-
线程 1 执行 CAS 操作,发现变量的值仍然是 A,认为没有变化,但实际上变量的值已经被修改过。
ABA 问题的解决方法
-
版本号:为变量添加一个版本号,每次修改变量时版本号加 1。
-
AtomicStampedReference:Java 提供了
AtomicStampedReference类,通过版本号解决 ABA 问题。
示例:
AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference<>(100, 0);
int[] stampHolder = new int[1];
int value = atomicStampedRef.get(stampHolder); // value = 100, stampHolder[0] = 0
atomicStampedRef.compareAndSet(100, 200, 0, 1); // true,当前值变为 200,版本号变为 1
329

被折叠的 条评论
为什么被折叠?



