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

相关推荐
NiNg_1_2341 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
Chrikk3 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*3 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue3 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man3 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
customer084 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
Yaml45 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠6 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#