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的工作原理及其局限性对编写高效、可扩展的并发应用至关重要。

相关推荐
Victor3565 小时前
https://editor.csdn.net/md/?articleId=139321571&spm=1011.2415.3001.9698
后端
Victor3565 小时前
Hibernate(89)如何在压力测试中使用Hibernate?
后端
灰子学技术6 小时前
go response.Body.close()导致连接异常处理
开发语言·后端·golang
Gogo8167 小时前
BigInt 与 Number 的爱恨情仇,为何大佬都劝你“能用 Number 就别用 BigInt”?
后端
fuquxiaoguang7 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
毕设源码_廖学姐8 小时前
计算机毕业设计springboot招聘系统网站 基于SpringBoot的在线人才对接平台 SpringBoot驱动的智能求职与招聘服务网
spring boot·后端·课程设计
野犬寒鸦10 小时前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
java·服务器·数据库·后端·学习·算法
逍遥德10 小时前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范
MX_935911 小时前
Spring的bean工厂后处理器和Bean后处理器
java·后端·spring
程序员泠零澪回家种桔子12 小时前
Spring AI框架全方位详解
java·人工智能·后端·spring·ai·架构