当前位置: 首页 > article >正文

多线程中的 CAS

CAS(Compare-And-Swap 或 Compare-And-Set) 是一种硬件层面的原子操作,用于解决多线程并发中的数据一致性问题。它允许在没有锁的情况下进行并发编程,是许多非阻塞算法(如无锁队列、无锁栈)的核心基础。

CAS 操作的基本思想是:比较并交换。即在更新某个变量时,先检查它的值是否符合预期,如果符合预期则进行更新,否则不进行更新。

CAS 是一种高效的、无锁的并发操作,它通过硬件支持的原子操作来避免锁竞争,适合用于并发环境中的共享数据更新。CAS 操作能够极大地提升多线程性能,但也有一些缺点,比如 ABA 问题和自旋可能带来的性能问题。在 Java 中,CAS 机制广泛应用于 java.util.concurrent.atomic 包中的类,帮助我们更高效地管理并发操作。

CAS 的工作原理

CAS 操作包含三个操作数:

内存中的值(V):当前变量在内存中的值。

期望值(E):我们期望该变量现在应该是的值。

新值(N):如果当前变量的值等于期望值,那么我们想把它更新为的新值。

CAS 操作执行的步骤如下:

检查内存中变量 V 的当前值是否等于 E(期望值)。如果等于 E,则将 V 更新为 N(新值)。如果不等于 E,则说明其他线程已经修改了该变量,CAS 操作失败,返回当前的 V 值。

CAS 是一种原子操作,在硬件级别上提供保障,因此多个线程可以同时进行比较并交换操作,而不需要锁来保证同步。

CAS 的应用

CAS 通常用于解决多线程环境下的共享资源竞争问题,避免使用锁机制带来的开销。Java 中提供了对 CAS 的直接支持,特别是在 java.util.concurrent.atomic 包中,例如 AtomicIntegerAtomicBooleanAtomicReference 等都使用了 CAS。

例如,AtomicInteger 类的 compareAndSet() 方法就是一个典型的 CAS 操作:

import java.util.concurrent.atomic.AtomicInteger;

public class CASTest {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);

        // 期望值为5,如果atomicInteger的当前值为5,则更新为10
        boolean isUpdated = atomicInteger.compareAndSet(5, 10);

        System.out.println("CAS 更新成功: " + isUpdated);
        System.out.println("当前值: " + atomicInteger.get());
    }
}

运行结果:

CAS 更新成功: true 当前值: 10

在上面的代码中,atomicInteger.compareAndSet(5, 10) 检查当前的值是否是5,如果是,则将其更新为10。由于初始值是5,因此操作成功,返回 true。如果另一个线程在此操作之前修改了 atomicInteger 的值,则 compareAndSet 会返回 false,而不会更新值。

CAS 的优点

无锁操作:CAS 是一种非阻塞算法,通过避免锁来提高并发性能,减少线程之间的竞争等待时间,适合高并发环境。

性能高效:CAS 操作是硬件层面的原子操作,因此比传统的加锁机制效率高,特别是在多线程争用激烈时,锁的开销会很大,而 CAS 通过自旋等待避免了锁的开销。

CAS 的缺点

虽然 CAS 有很多优势,但它也存在一些缺点:

ABA 问题

Java 提供了 AtomicStampedReference 来解决 ABA 问题,通过引入版本号或时间戳,记录每次修改的时间戳,防止 ABA 问题的出现。

示例:

import java.util.concurrent.atomic.AtomicStampedReference;

public class ABAProblemTest {
    public static void main(String[] args) {
        AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference<>(100, 1);

        int[] stampHolder = new int[1];
        Integer ref = atomicStampedRef.get(stampHolder);

        System.out.println("初始值: " + ref + ", 初始版本: " + stampHolder[0]);

        // 修改值为101
        atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
        // 再次修改回100
        atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);

        // 尝试使用原来的版本号来更新值
        boolean result = atomicStampedRef.compareAndSet(100, 101, 1, 2);  // 版本号不匹配,更新失败
        System.out.println("更新成功: " + result);
    }
}

ABA 问题是指:一个变量的值从 A 变成 B,然后又变回 A。对于 CAS 操作,它只会检查当前值是否与预期值相同,但不关心该值在此过程中是否发生了变化。这可能导致错误的判断,认为变量的值没有被修改。

自旋导致 CPU 资源浪费

CAS 操作是通过不断重试(自旋)来尝试更新的。如果多个线程频繁竞争同一个变量的更新,可能导致大量的自旋操作,从而消耗大量的 CPU 资源。

只能对单个变量操作

CAS 只能保证单个变量的原子性操作,无法直接保证多个变量的原子性。如果要操作多个共享变量,就需要借助锁或其他同步机制。


http://www.kler.cn/news/350638.html

相关文章:

  • 【问题解决】C++调用shared_from_this()报错bad_weak_ptr解决方案
  • 爱奇艺大数据多 AZ 统一调度架构
  • Github 2024-10-25 Java开源项目日报 Top8
  • 批量修改YOLO格式的标注类别
  • 《Sui区块链:重塑去中心化应用的新星与未来潜力》
  • Aloudata BIG 主动元数据平台支持 Oracle/DB2 存储过程算子级血缘解析
  • 腾讯云视立方Electron 相关问题
  • x86架构与arm架构
  • 详解java8的新特性
  • 【Next.js 项目实战系列】05-删除 Issue
  • Python--plt.errorbar学习笔记
  • 视频网站后端架构:Spring Boot的创新应用
  • 【代码随想录Day44】动态规划Part12
  • Python do while 实现案例
  • 使用CSS+SVG实现加载动画
  • SpringCloudAlibaba升级手册
  • Finops成本优化企业实践-可规划篇
  • linux线程 | 线程的控制(下)
  • linux下在线安装MySQL-华为云服务器
  • 【WebLogic】Oracle发布2024年第四季度中间件安全公告
  • Sharding-JDBC标准模式详解
  • Java基础:面向对象编程5
  • 恢复已删除文件的 10 种安卓数据恢复工具
  • IRP默认最小流程
  • 2023年“网络建设与运维”广西省赛试题复盘
  • yakit使用教程(四,信息收集)