CAS的理解

CAS(Compare-And-Swap)是一种重要的无锁的原子算法,它在多线程环境下使用硬件原语来同步对共享数据的访问。CAS操作通常由现代CPU直接支持,并在操作系统和编程语言的并发库中广泛应用。

CAS的基本概念

CAS操作包含三个操作数:

  • 内存位置(内存中的特定地址)
  • 预期原值(预期读取到的值)
  • 新值(需要写入的值)

算法流程:

  1. 从内存位置读取当前值。
  2. 比较当前值与预期原值是否相等。
    • 如果相等,将新值写入内存位置。
    • 如果不相等,不做任何操作。

CAS的Java实现

在Java中,java.util.concurrent.atomic包提供了一系列的原子类来支持无锁的线程安全编程。下面是AtomicInteger中的incrementAndGet方法的简化版实现,这个方法通过CAS原子性地递增变量的值:

java 复制代码
public final class AtomicInteger extends Number implements java.io.Serializable {
    // Unsafe mechanics
    private static final sun.misc.Unsafe unsafe = sun.misc.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;

    //...其他方法和构造函数

    public final int incrementAndGet() {
        int current;
        int next;
        do {
            current = unsafe.getIntVolatile(this, valueOffset); // 读取当前值
            next = current + 1; // 计算新值
        } while (!unsafe.compareAndSwapInt(this, valueOffset, current, next)); // CAS操作
        return next;
    }

    //...其他方法和构造函数
}

在这个例子中,incrementAndGet使用CAS循环来更新value字段。如果在尝试更新时其他线程已经更改了value,循环会持续不断地尝试,直到成功为止。

CAS的细节和问题

尽管CAS是非阻塞的,但它并不完美。在实际应用中,有以下几个注意点:

  1. ABA问题

    ABA问题发生在一个变量从A变化到B,然后又变回A,CAS将无法感知中间发生的变化。为解决这一问题,可以使用带版本号的CAS,如AtomicStampedReference类。

  2. 循环时间长开销大

    当多个线程尝试对同一个变量执行CAS时,可能会引发激烈的竞争。如果一个线程一直无法成功更新变量,会导致高CPU负载。

  3. 只能保证一个共享变量的原子操作

    CAS仅能对单一的共享变量进行原子操作。当涉及到多个共享变量时,可以使用锁或者java.util.concurrent包下的原子类,如AtomicReference。

CAS的代码示例

下面是一个简单的基于CAS的计数器实现:

java 复制代码
import java.util.concurrent.atomic.AtomicInteger;

public class CASCounter {
    private AtomicInteger count = new AtomicInteger(0);

    public int increment() {
        int oldValue, newValue;
        do {
            oldValue = count.get(); // 获取当前的值
            newValue = oldValue + 1; // 期望递增后的值
        } while (!count.compareAndSet(oldValue, newValue)); // CAS操作
        return newValue;
    }

    public int getValue() {
        return count.get();
    }
}

在这个例子中,increment()方法不断地尝试CAS操作,直到成功。compareAndSet是一个原子操作,如果当前值与预期值oldValue相同,就把它设置为newValue。

总结

CAS是并发编程中的关键技术,特别是在编写无锁数据结构时。它能够减少锁的使用,从而减少线程之间的阻塞和上下文切换。然而,CAS不是完全没有代价的,它可能导致高CPU负载,并需要仔细处理ABA问题。通过Java中的java.util.concurrent.atomic包,可以轻松地实现基于CAS的线程安全操作。了解CAS的工作原理及其局限性对编写高效、可扩展的并发应用至关重要。

相关推荐
利威尔xu6 分钟前
Go IM 源码实战(13):30 个会话只有 2 个红点,未读数错在三条路径
redis·后端·微服务·面试·golang
薛定谔的猫-菜鸟程序员42 分钟前
软件架构设计师开发资料大全(从入门到精通)
后端·python·系统架构·flask·架构设计师
IT_陈寒2 小时前
SpringBoot多环境配置把我坑惨了,这个踩坑经验你最好看看
前端·人工智能·后端
码事漫谈3 小时前
DeepSeek 搞了个 "Super-agent",但不是超级智能
后端
吴凡wudianmei3 小时前
从 0 搭一个 AI Agent(四):SSE 跑通之后,三处“契约错配”与它们的修复边界
前端·后端·aigc
小兔子3 小时前
defer 的参数在写下那一刻就求值了,循环里这样写会踩坑
后端
苍何3 小时前
腾讯 Buddy One 来了,办公生态一键打通 WorkBuddy(附教程)
后端
Tokenfree3 小时前
把通义千问接进工单系统:三个版本够用,但有两个坑没人提前告诉我
后端
wei_shuo3 小时前
MongoDB迁移实战:基于SQL的文档数据开发与多模态融合实践
后端
小兔子3 小时前
os.path.join 遇到绝对路径会丢掉前面所有段,这条规则写在文档第二句
后端