Java 中 CAS 是什么,有哪些实际应用场景

CAS 是什么

CAS(Compare And Swap)是一种并发编程中的原子操作,用于实现多线程之间的同步。在 Java 中,CAS 操作通过 sun.misc.Unsafe 类实现。

CAS 操作是一种乐观锁机制,它假设对于共享变量的修改操作不会引起冲突,因此在执行操作之前不会进行加锁操作。CAS 操作包含三个参数:共享变量的内存地址 V、期望值 A 和新值 B。如果当前共享变量的值等于期望值 A,则将共享变量的值设置为新值 B,否则不做任何操作。CAS 操作是原子的,即整个操作要么成功执行,要么不执行。

CAS 操作的作用是实现线程之间的同步,避免多个线程同时修改共享变量而导致的数据不一致问题。在 Java 中,CAS 操作通常用于实现锁、计数器等高并发场景中。CAS 操作具有以下优点:

  1. 高效性:CAS 操作不需要加锁,因此可以避免加锁操作所带来的性能开销。
  2. 原子性:CAS 操作是原子的,因此可以保证操作的一致性。
  3. 无阻塞: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 占用过高,从而影响程序的性能。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2013crazy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值