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

相关推荐
kingwebo'sZone1 分钟前
ASP.net WebAPI 上传图片实例(保存显示随机文件名)
后端·asp.net
桑榆肖物2 分钟前
一个简单的ASP.NET 一致性返回工具库
后端·asp.net
组态软件3 小时前
web组态软件
前端·后端·物联网·编辑器·html
Peter_chq3 小时前
【计算机网络】多路转接之select
linux·c语言·开发语言·网络·c++·后端·select
cnsxjean6 小时前
SpringBoot集成Minio实现上传凭证、分片上传、秒传和断点续传
java·前端·spring boot·分布式·后端·中间件·架构
kingbal7 小时前
SpringCloud:Injection of resource dependencies failed
后端·spring·spring cloud
刘天远7 小时前
django实现paypal订阅记录
后端·python·django
ℳ₯㎕ddzོꦿ࿐7 小时前
Spring Boot集成MyBatis-Plus:自定义拦截器实现动态表名切换
spring boot·后端·mybatis
逸风尊者8 小时前
开发也能看懂的大模型:RNN
java·后端·算法
小钟不想敲代码9 小时前
第4章 Spring Boot自动配置
java·spring boot·后端