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

相关推荐
麦聪聊数据10 小时前
LiveOps事故零容忍:游戏行业数据库的细粒度权限管控与审计实践
运维·数据库·后端·sql
shepherd12610 小时前
深度剖析SkyWalking:从内核原理到生产级全链路监控实战
分布式·后端·skywalking
橘子师兄10 小时前
C++AI大模型接入SDK—Genimi接入封装
c++·人工智能·后端
码农水水10 小时前
大疆Java面试被问:使用Async-profiler进行CPU热点分析和火焰图解读
java·开发语言·jvm·数据结构·后端·面试·职场和发展
ahauedu10 小时前
SpringBoot 3.5.10引入springdoc-openapi-starter-webmvc-ui版本
java·spring boot·后端
未来龙皇小蓝10 小时前
Spring内置常见线程池配置及相关概念
java·后端·spring·系统架构
新缸中之脑11 小时前
Google:Rust实战评估
开发语言·后端·rust
一 乐11 小时前
在线考试|基于springboot + vue在线考试系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
女王大人万岁11 小时前
Go标准库 path 详解
服务器·开发语言·后端·golang
qq_124987075311 小时前
基于spring boot的调查问卷系统的设计与实现(源码+论文+部署+安装)
java·vue.js·spring boot·后端·spring·毕业设计·计算机毕业设计