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

CAS(Compare And Swap)

CAS核心原理

操作流程

CAS 包含三个参数:内存值(V)预期值(E)和新值(N)。执行步骤如下:

  • 比较:检查当前内存值 V 是否等于预期值 E。

  • 交换:如果相等,将内存值更新为新值 N;否则不操作。

  • 返回结果:返回操作是否成功(或当前实际值)。整个过程是原子性的,由 CPU 指令直接保证,例如 Intel 的 cmpxchg 指令。

    AtomicInteger atomicInt = new AtomicInteger(5);
    boolean success = atomicInt.compareAndSet(5, 10); // 若当前值为5,则更新为10
    //若 atomicInt 的当前值未被其他线程修改,则更新成功(返回 true),否则失败(返回false)
    

原子性保障

CPU 通过两种机制保证 CAS 的原子性:

总线锁定

  • 使用 LOCK# 信号锁定总线,阻止其他 CPU 访问内存,确保当前操作独占执行。
  • 缺点:锁定期间其他 CPU 无法操作内存,性能开销大

缓存锁定

  • 仅锁定缓存行(CPU 高速缓存的最小单位),通过 缓存一致性协议(如 MESI)检测共享变量是否被修改。
  • 优势:缩小锁定范围,减少阻塞,现代 CPU 普遍采用此方式。

应用场景

原子类Atomic

//Java 的 AtomicInteger、AtomicReference 等类通过 CAS 实现线程安全的原子操作。
public final int incrementAndGet() {
    for (;;) {
        int current = get();           // 当前值
        int next = current + 1;        // 新值
        if (compareAndSet(current, next)) 
            return next;               // 更新成功则返回
    }
}
//自旋循环中不断尝试 CAS,直到成功。

并发容器

  • ConcurrentHashMap:初始化数组时通过 sizeCtl 变量和 CAS 确保只有一个线程执行初始化,其他线程自旋等待或调用 Thread.yield() 让出 CPU
  • ConcurrentLinkedQueue:无锁队列通过 CAS 实现入队和出队的线程安全

自旋锁和无锁算法

//自旋锁:线程通过循环尝试 CAS 获取锁,避免阻塞和上下文切换。例如:
while (!lock.compareAndSet(0, 1)) { /* 自旋 */ }
//非阻塞数据结构:如无锁栈、队列等,依赖 CAS 实现并发安全

优缺点

在这里插入图片描述

ABA问题和解决方案

问题描述

  • 线程 1 读取值 A,线程 2 将 A→B→A,线程 1 的 CAS 仍会成功,导致逻辑错误(例如链表头节点被意外删除)

解决方案

//版本号机制:每次更新递增版本号,检查值的同时检查版本号。
//AtomicStampedReference:Java 提供带版本号的原子类。
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 1);
ref.compareAndSet(100, 200, 1, 2); // 版本号从1→2
```[2](@ref) [4](@ref)

java中的cas实现

  1. Unsafe类:CAS 操作通过 Unsafe 类调用本地方法(如 compareAndSwapInt),直接操作内存地址
  2. 底层指令映射:JVM 将 compareAndSet 映射为 CPU 的 cmpxchg 指令,确保原子性

总结

  • CAS 是一种高效的无锁并发控制机制,适用于计数器、轻量级锁等场景。其核心优势是避免线程阻塞,但需注意 ABA 问题和自旋开销。在高并发系统中,CAS 常与退让策略(指数退避)(如 Thread.yield())结合,平衡性能与资源消耗
    • 指数退避:指数退避是一种动态调整重试延迟时间的算法,旨在通过指数级增长的等待时间和随机抖动减少系统资源竞争或网络冲突,提升系统的稳定性和效率。其核心思想是:在每次尝试失败后,延迟时间成倍增加,从而避免因频繁重试导致的资源过载或冲突加剧。
    • 核心原理:延迟时间指数增长、随机抖动、最大延迟限制
      • 延迟时间指数增长:初始设定一个最小延迟(如 100ms),每次失败后延迟时间按指数倍数(如 2 倍)增长。例如:第 1 次重试延迟 100ms,第 2 次 200ms,第 3 次 400ms,依此类推。
      • 随机抖动(Jitter):在延迟时间中加入随机值(如 0~100ms),防止多个请求在同一时间点集中重试,避免“雪崩效应”
        200ms,第 3 次 400ms,依此类推。
      • 随机抖动(Jitter):在延迟时间中加入随机值(如 0~100ms),防止多个请求在同一时间点集中重试,避免“雪崩效应”
      • 最大延迟限制:设置上限(如 30 秒),防止无限等待
http://www.dtcms.com/a/91954.html

相关文章:

  • 邮箱验证:避免无效邮件浪费营销资源
  • 【数据结构】_单链表_相关面试题(二)
  • Focal Loss
  • C++中ShellExecute函数使用方法说明,如果一开始参数为隐藏,后面还能再显示出来吗
  • liunx安装宝塔(可自定义网站)
  • FLEXlm如何通过web 管理
  • PyTorch量化技术教程:第五章 综合实战项目
  • FFmpeg学习:AVStream AVCodecParameters
  • 服务器磁盘卷组缓存cache设置介绍
  • 深入理解指针(3)(C语言版)
  • 工业如何数字化转型
  • 基于 Swoole 的高性能 RPC 解决方案
  • linux常用指令(9)
  • element-plus中,Loading 加载组件的使用
  • Unity粒子系统
  • 有约束的确定型存贮模型及其MATLAB实现
  • PHP 应用MYSQL 架构SQL 注入跨库查询文件读写权限操作
  • 鸿蒙NEXT开发App相关工具类
  • 简单有效的编辑AI交互 Prompt(提示)
  • ElasticSearch -- 部署完整步骤
  • 关于cmd中出现无法识别某某指令的问题
  • Microi吾码界面设计引擎之基础组件用法大全【内置组件篇·上】
  • 【AI学习】概念了解
  • 5.Excel:从网上获取数据
  • JavaScript 改变 HTML 图像
  • Axure项目实战:智慧城市APP(七)我的、消息(显示与隐藏交互)
  • BlockingQueue遇到活锁问题
  • ORA-00600 [2662]
  • linux - centos7 部署 redis6.0.5
  • Elasticsearch 的搜索功能