CAS(Compare-And-Swap 或 Compare-And-Set)是一种用于实现并发编程中无锁(lock-free)数据结构的原子操作。CAS 操作比较内存中的某个位置的当前值是否等于预期值,如果相等,则将其更新为新的值,否则不更新。整个过程是一个原子操作,不会被中断,从而避免了线程同步中的竞争和死锁问题。
CAS 的原理
CAS 操作包含三个操作数:
- 内存位置(V,Variable):需要更新的变量的内存地址。
- 期望值(A,Expected):当前线程认为这个变量应该具有的值。
- 新值(B,New):如果变量的当前值等于期望值,那么需要将变量更新为的新值。
CAS 操作的逻辑如下:
- 如果内存位置 V 的当前值等于期望值 A,那么将内存位置 V 的值更新为新值 B,返回 true 表示更新成功。
- 否则,不做任何操作,返回 false 表示更新失败。
CAS 的步骤
- 读取变量 V 的当前值。
- 比较变量 V 的当前值和期望值 A。
- 如果变量 V 的当前值等于期望值 A,将其更新为新值 B。
- 如果变量 V 的当前值不等于期望值 A,不进行更新。
这个过程是通过底层硬件的原子指令支持来实现的,因此是线程安全的。
CAS 的优点
- 无锁并发:通过 CAS 操作,可以实现无锁的数据结构,从而避免了传统锁机制带来的开销和死锁问题。
- 高效:在大多数情况下,CAS 操作比使用锁进行同步更加高效,特别是在多线程竞争不激烈的情况下。
CAS 的缺点
- ABA 问题:如果变量 V 的值从 A 变成 B,又变回 A,CAS 操作无法检测到这种变化,从而误认为没有发生变化。ABA 问题可以通过版本号解决,即每次更新变量时同时更新版本号。
- 自旋等待:在高竞争环境下,如果多个线程频繁失败并重试 CAS 操作,会导致大量的 CPU 消耗。
- 复杂性:相较于使用锁,编写和维护 CAS 操作的代码更为复杂。
Java 中的 CAS 实现
Java 提供了 java.util.concurrent.atomic
包中的类来支持 CAS 操作,如 AtomicInteger
、AtomicBoolean
、AtomicReference
等。这些类内部使用了 Unsafe 类的 CAS 方法来实现原子操作。
示例:AtomicInteger 的 CAS 操作
import java.util.concurrent.atomic.AtomicInteger;public class CASExample {public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(0);int expectedValue = 0;int newValue = 1;boolean success = atomicInteger.compareAndSet(expectedValue, newValue);System.out.println("CAS operation success: " + success);System.out.println("Current value: " + atomicInteger.get());}
}
解决 ABA 问题
Java 提供了 AtomicStampedReference
类来解决 ABA 问题。它在进行 CAS 操作时,不仅比较值,还比较一个额外的标记(stamp)。
示例:使用 AtomicStampedReference 解决 ABA 问题
import java.util.concurrent.atomic.AtomicStampedReference;public class ABAExample {public static void main(String[] args) {AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference<>(100, 0);int[] stampHolder = new int[1];Integer currentValue = atomicStampedRef.get(stampHolder);int currentStamp = stampHolder[0];int newValue = 101;int newStamp = currentStamp + 1;boolean success = atomicStampedRef.compareAndSet(currentValue, newValue, currentStamp, newStamp);System.out.println("CAS operation success: " + success);System.out.println("Current value: " + atomicStampedRef.get(stampHolder) + ", Current stamp: " + stampHolder[0]);}
}
总结
CAS 是一种高效的无锁并发机制,通过硬件支持的原子操作来实现线程安全。虽然 CAS 有一些缺点,但在合适的场景中,CAS 可以提供比锁更好的性能和可扩展性。Java 提供了丰富的 CAS 支持类,开发者可以方便地使用这些类来实现高效的并发程序。