CAS 是什么
CAS(Compare And Swap)是一种并发编程中的原子操作,用于实现多线程之间的同步。在 Java 中,CAS 操作通过 sun.misc.Unsafe 类实现。
CAS 操作是一种乐观锁机制,它假设对于共享变量的修改操作不会引起冲突,因此在执行操作之前不会进行加锁操作。CAS 操作包含三个参数:共享变量的内存地址 V、期望值 A 和新值 B。如果当前共享变量的值等于期望值 A,则将共享变量的值设置为新值 B,否则不做任何操作。CAS 操作是原子的,即整个操作要么成功执行,要么不执行。
CAS 操作的作用是实现线程之间的同步,避免多个线程同时修改共享变量而导致的数据不一致问题。在 Java 中,CAS 操作通常用于实现锁、计数器等高并发场景中。CAS 操作具有以下优点:
- 高效性:CAS 操作不需要加锁,因此可以避免加锁操作所带来的性能开销。
- 原子性:CAS 操作是原子的,因此可以保证操作的一致性。
- 无阻塞:CAS 操作不会阻塞线程,因此可以避免线程的切换和上下文切换带来的开销。
代码示例
以下是一个利用 CAS 操作实现线程安全计数器的示例代码:
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class CASCounter {
private volatile long count = 0;
private static final Unsafe unsafe;
private static final long countOffset;
static {
try {
// 使用反射获取 Unsafe 实例
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
// 获取 count 字段的内存地址
countOffset = unsafe.objectFieldOffset(CASCounter.class.getDeclaredField("count"));
} catch (Exception ex) {
throw new Error(ex);
}
}
public void increment() {
long current;
do {
// 获取当前 count 的值
current = unsafe.getLongVolatile(this, countOffset);
} while (!unsafe.compareAndSwapLong(this, countOffset, current, current + 1));
}
public long getCount() {
return count;
}
}
在上面的代码中,我们使用 volatile 关键字修饰 count 变量,以保证其可见性。在 increment() 方法中,我们使用 do-while 循环来不断获取当前 count 的值,直到 CAS 操作成功为止。如果 CAS 操作失败,则重试获取当前 count 的值,直到 CAS 操作成功为止。在 getCount() 方法中,我们直接返回 count 变量的值。由于 CAS 操作是原子的,因此在多线程并发操作下,CASCounter 类可以保证计数器的正确性和线程安全性。
应用场景
一个常见的使用 CAS 操作的应用场景是实现自旋锁。自旋锁是一种基于忙等待的锁机制,它的特点是在获取锁的过程中不会释放 CPU 时间片,而是一直等待锁的释放。自旋锁适用于锁竞争不激烈、持有锁的时间短的场景,可以避免线程的上下文切换和调度带来的开销,从而提高程序的性能。
在 Java 中,自旋锁通常使用 CAS 操作实现。下面是一个使用 CAS 操作实现自旋锁的示例代码:
import java.util.concurrent.atomic.AtomicReference;
public class SpinLock {
private AtomicReference<Thread> owner = new AtomicReference<>();
public void lock() {
Thread currentThread = Thread.currentThread();
while (!owner.compareAndSet(null, currentThread)) {
// 如果 CAS 操作失败,则一直自旋等待
}
}
public void unlock() {
Thread currentThread = Thread.currentThread();
if (!owner.compareAndSet(currentThread, null)) {
throw new IllegalStateException("Lock is not held by the current thread");
}
}
}
在上面的代码中,我们使用 AtomicReference 类来实现自旋锁。owner 变量保存当前持有锁的线程,初始值为 null。在 lock() 方法中,我们使用 compareAndSet() 方法尝试将 owner 的值设置为当前线程,如果 CAS 操作失败,则说明锁已被其他线程持有,当前线程需要不断自旋等待。在 unlock() 方法中,我们首先获取当前持有锁的线程,然后使用 compareAndSet() 方法将 owner 的值设置为 null,如果 CAS 操作失败,则说明当前线程不是持有锁的线程,抛出异常。
小结
总的来说,使用 CAS 操作实现自旋锁可以避免线程的上下文切换和调度带来的开销,从而提高程序的性能。但需要注意的是,自旋锁适用于锁竞争不激烈、持有锁的时间短的场景。如果锁竞争激烈或者持有锁的时间长,自旋锁会导致 CPU 占用过高,从而影响程序的性能。