CAS(Compare and Swap,比较并交换) 并非 Java 语言特有的概念,而是现代计算机硬件提供的一条核心原子指令。在 Java 并发编程中,它扮演着“幕后英雄”的角色,是构建高性能、无锁并发工具(如原子类、并发集合和高级锁)的原子性基石。理解 CAS 是掌握 Java 高并发编程的关键。

一、CAS 是什么?硬件指令的 Java 面孔

  • 硬件本质: CAS 的核心是处理器(CPU)直接支持的一条原子指令(例如 x86 架构的 CMPXCHG)。这条指令保证“读取内存位置的值 -> 比较该值是否等于预期值 -> 如果相等则写入新值”这一系列操作在一个不可中断的 CPU 总线周期内完成,提供硬件级的原子性保障。

  • Java 的访问方式: Java 开发者无法直接调用 CPU 指令。Java 通过以下路径间接利用 CAS:

    1. java.util.concurrent.atomic API (最常用): 如 AtomicInteger.compareAndSet(expectedValue, newValue)

    2. java.lang.invoke.VarHandle (Java 9+, 推荐): 提供更安全、标准化的内存访问和 CAS 操作。

    3. sun.misc.Unsafe (Java 9 前, 不推荐): 历史遗留的“后门”,直接操作内存。

  • JNI 桥梁: 无论通过 AtomicXxxVarHandle 还是 Unsafe,最终对 CAS 的调用都会通过 Java Native Interface (JNI) 进入本地(Native)代码(C/C++)。本地代码会调用编译器内建函数或内联汇编,这些代码最终被编译成目标 CPU 平台的 CAS 指令(如 CMPXCHG)。因此,CAS 操作在 Java 中是通过 JNI 调用最终执行的硬件指令。

  • 核心语义: 抽象为一个函数:

    boolean simulatedCAS(Object memoryLocation, Object expectedValue, Object newValue) {// 以下三步在硬件指令层面是原子的、不可分割的!Object currentValue = readMemory(memoryLocation); // 1. 读取内存当前值if (currentValue == expectedValue) {             // 2. 比较当前值是否等于预期值writeMemory(memoryLocation, newValue);      // 3. 如果相等,则写入新值return true; // CAS 成功}return false; // CAS 失败(值已被其他线程修改)
    }

二、CAS 的原理:硬件、JNI 与 Java 的协作

  1. 硬件层: CPU 提供 CMPXCHG 等原子指令,确保比较和交换操作的原子性。涉及 CPU 缓存一致性协议(如 MESI)保证多核间的数据可见性。

  2. JNI 层: Java 虚拟机(JVM)通过本地方法声明暴露 CAS 能力。当 Java 代码调用 AtomicInteger.compareAndSet() 时:

    • JVM 找到对应的本地方法实现。

    • 通过 JNI 接口调用到 C/C++ 编写的本地函数。

    • 本地函数执行平台相关的代码(调用 __atomic_compare_exchange 等内建函数或内联汇编),最终生成目标 CPU 的 CAS 指令。

  3. Java API 层:

    • AtomicXxx 类: 封装 CAS 操作,提供易用的 get()set()compareAndSet()getAndIncrement() 等方法。getAndIncrement() 内部通常是一个 CAS 自旋循环。

    • VarHandle Java 9+ 引入,提供对字段进行各种原子操作(包括 CAS)的标准、类型安全且具有访问控制的方式。是 Unsafe 的现代替代品。

    • 高级锁与同步器: ReentrantLockSemaphoreCountDownLatch 等依赖于 AbstractQueuedSynchronizer (AQS)。AQS 的核心状态变量 state 是一个 volatile int,其获取和释放操作高度依赖 CAS 来尝试无锁地修改状态。CAS 是这些锁实现内部快速路径(fast-path)的核心机制。

三、CAS 有什么用?无锁并发的引擎

  1. 实现无锁(Lock-Free)算法: 允许线程在不使用阻塞锁(如 synchronized 或 ReentrantLock.lock())的情况下安全地更新共享数据。这是其最核心的价值

  2. 构建高性能并发工具:

    • 原子类 (AtomicIntegerAtomicLongAtomicReference): 提供线程安全的计数器、标志位、对象引用更新。

    • 无锁数据结构: ConcurrentLinkedQueue (无锁队列), ConcurrentHashMap (JDK8+ 使用 CAS 优化桶操作)。

    • 高级锁与同步器基础 (AQS): ReentrantLockReentrantReadWriteLockSemaphoreCountDownLatch 等内部状态 (state) 的更新都深度依赖 CAS 来高效处理无竞争或低竞争场景。

  3. 替代部分锁场景: 在简单操作(如计数器递增 i++)或状态标志更新时,使用 CAS(通过 AtomicXxx)比使用锁性能更高,避免了线程阻塞、上下文切换和锁竞争的开销。

四、CAS 的优缺点:硬币的两面

  • 优点:

    • 高性能(低/中竞争): 避免线程阻塞和上下文切换。在低竞争场景下,性能远超传统锁。

    • 无死锁: 不涉及锁获取,从根本上避免死锁(但需注意活锁/饥饿)。

    • 可扩展性: 线程数增加时,性能下降通常比锁更平缓。

  • 缺点:

    • ABA 问题: 线程1读取值 A,线程2将值改为 B 后又改回 A。线程1进行 CAS 时发现值仍是 A,认为未被修改而成功,但中间状态 B 可能已产生影响(例如链表头被移除又加回)。解决方案: AtomicStampedReference (值+版本戳) 或 AtomicMarkableReference (值+布尔标记)。

    • 自旋开销(高竞争): 如果多个线程频繁竞争同一变量,失败的线程会不断重试(自旋),浪费 CPU 资源。极端高竞争下性能可能不如锁(锁会让失败线程挂起)。

    • 单一变量原子性: CAS 仅能保证对单个共享变量操作的原子性。需要原子性更新多个变量时,仍需锁或其他机制。

    • 实现复杂度: 构建正确的无锁数据结构(如队列、栈)比基于锁的实现复杂得多,容易引入微妙错误。

五、CAS 的优化:应对挑战

  1. 减少竞争开销:

    • LongAdder / DoubleAdder (Java 8+): 针对高并发计数场景。内部维护一个 Cell 数组(分散热点)。线程优先更新自己可能关联的 Cell,最后通过 sum() 合并结果。写性能远高于高竞争下的 AtomicLong

    • 自适应自旋: JVM 或库可能根据历史 CAS 成功率动态调整失败线程的自旋次数。

  2. 解决 ABA 问题:

    • AtomicStampedReference 将值 V 与一个 int 版本戳绑定。CAS 需同时检查值和版本戳。

    • AtomicMarkableReference 将值 V 与一个 boolean 标记绑定。

  3. 更优硬件指令利用: JVM 会为目标平台选择最高效的 CAS 实现。

  4. VarHandle 的灵活性: 提供更细粒度的内存排序控制(acquirerelease 等语义),有时能生成更优化的代码。

六、使用 CAS 的注意点

  1. 警惕 ABA: 评估业务逻辑是否允许值“回头”。如果不允许,必须使用带版本戳或标记的原子引用。

  2. 权衡竞争强度: 低/中竞争是 CAS 的主场。高竞争时,优先考虑 LongAdder、锁(synchronizedReentrantLock)或尝试减少共享变量争用(如数据分片)。

  3. 避免重复造轮子: 优先使用成熟的 java.util.concurrent 工具类 (AtomicXxxConcurrentHashMapLongAdder)。自行实现无锁算法风险高。

  4. 理解内存可见性: CAS 操作本身具有 volatile 读写的内存语义,能保证变量的可见性。使用 VarHandle 时可更精确控制内存屏障。

  5. 平台差异: 虽然 Java API 统一,但底层 CAS 性能在不同 CPU 上有差异(通常 JVM 会优化处理)。

七、典型使用场景

  1. 计数器/统计: AtomicIntegerAtomicLongLongAdder (高并发计数首选)。

  2. 状态标志位: AtomicBoolean 或 volatile + CAS 更新。

  3. 构建无锁数据结构: ConcurrentLinkedQueueConcurrentHashMap (桶操作、树结构调整)。

  4. 实现锁与同步器 (AQS): ReentrantLock 获取/释放锁时的 state 更新。

  5. 单例模式 (双重检查锁定优化):

    public class Singleton {private static volatile Singleton instance; // volatile 保证可见性和部分有序性public static Singleton getInstance() {Singleton localRef = instance;if (localRef == null) {                // 第一次检查 (非同步,快速路径)synchronized (Singleton.class) {   // 同步块localRef = instance;if (localRef == null) {         // 第二次检查 (同步块内)instance = localRef = new Singleton(); // 创建实例}}}return localRef;}private Singleton() {}
    }

    (虽然这个经典例子主要展示 volatile 和 synchronized,但在 AQS 实现锁时,其内部的 state 操作就是 CAS 的典型应用)

八、演变过程:从硬件到安全的 Java API

  1. 硬件指令诞生: CPU 厂商提供 CAS 指令。

  2. Unsafe 与 JUC 崛起 (Java 1.5): 通过 sun.misc.Unsafe 的 JNI 封装暴露 CAS,支撑了 java.util.concurrent (JUC) 包的原子类和并发集合。

  3. 原子类封装 (Java 5): AtomicIntegerAtomicReference 等提供易用的 CAS API。

  4. ABA 解决方案 (Java 5): AtomicStampedReference 引入版本戳。

  5. 高并发计数优化 (Java 8): LongAdderDoubleAdder 解决 AtomicLong 高竞争瓶颈。

  6. 标准化与安全化 (Java 9+): java.lang.invoke.VarHandle 作为 Unsafe 中 CAS 等操作的现代、安全替代品,提供更强的类型安全性和访问控制。

九、优秀设计:构建在 CAS 之上

  1. java.util.concurrent.atomic 包: 完美封装底层 CAS 复杂性,提供直观、线程安全的原子操作。

  2. LongAdder/DoubleAdder 空间换时间,通过分散竞争点(Cell[])实现高并发写场景下的卓越吞吐量,是优化思想的典范。

  3. AQS (AbstractQueuedSynchronizer): 并发库的“心脏”。巧妙结合 CAS 和 CLH 队列:

    • CAS (快速路径): 尝试无锁地获取/释放同步状态 (state)。

    • CLH 队列: 当 CAS 快速路径失败(有竞争),将线程包装成节点入队管理,进行阻塞或等待唤醒。

    • 这种设计使得在无竞争或低竞争时性能极高(只需 CAS),高竞争时也能公平有效地管理线程。ReentrantLockSemaphore 等都是基于 AQS 构建。

  4. VarHandle 代表未来方向,提供类型安全、可预测内存语义 (acquire/release)、受控访问的 CAS 及其他内存操作,是编写高性能、安全并发代码的基础。

十、性能:场景决定成败

  • 低/无竞争: 性能王者! 开销 ≈ 几次内存访问 + JNI/CAS 指令本身。远低于线程阻塞/唤醒开销。

  • 中等竞争: 通常优于锁。 自旋消耗 CPU,但避免了上下文切换。总体吞吐量较高。

  • 高竞争: 性能可能急剧下降! 大量 CPU 浪费在失败的自旋上。此时:

    • LongAdder > AtomicLong >> 纯 CAS 自旋

    • 锁 (synchronized/ReentrantLock): 可能成为更好选择,因为失败线程会挂起,释放 CPU 资源给其他线程。

  • 黄金法则: 性能测试!性能测试!性能测试! 实际性能高度依赖于具体场景(竞争强度、操作耗时、硬件)。

十一、个人理解:无锁的利器与权衡

CAS 是 Java 高并发编程不可或缺的“原子武器”。它揭示了并发控制的本质:在硬件支持下,通过乐观的“尝试-失败-重试”机制避免昂贵的锁开销。它是 JUC 包高效能的秘密源泉,驱动着 AQS 构建的强大锁与同步器。

然而,“没有免费的午餐”。CAS 在低竞争时是性能利器,但在高竞争时可能适得其反。ABA 问题如同暗礁,需要开发者时刻警惕。LongAdder 和 VarHandle 的出现,展示了 Java 社区在易用性、安全性和性能之间持续寻求的平衡。

选择 CAS 还是锁?这绝非教条,而是一门权衡的艺术。核心在于精准评估共享数据的竞争强度。理解 CAS 的原理、优缺点及优化手段(尤其是 LongAdder),是 Java 开发者迈向高阶并发的必经之路。

十二、未来变化趋势:持续演进

  1. VarHandle 成为主流: 随着旧 Java 版本淘汰,Unsafe 的直接 CAS 访问将淡出,VarHandle 将成为执行 CAS 操作的标准、安全方式。

  2. 精细化内存控制: VarHandle 提供的 acquire/release 等内存排序语义,允许开发者更精确地控制内存可见性和顺序,减少不必要的内存屏障,为编写更高效的无锁代码提供可能。

  3. 硬件原语进化: CPU 厂商可能提供更强大的原子原语(如 LL/SC 的增强版、范围 CAS、有限的事务内存支持)。Java 将通过标准 API(如 VarHandle 扩展)集成这些能力。

  4. 与 Project Loom (虚拟线程) 协同: 虽然虚拟线程主要解决 I/O 阻塞问题,但其调度器内部以及与平台线程交互的关键临界区,仍将依赖 CAS 等高效无锁操作来保证原子性和最小化开销。CAS 在虚拟线程时代依然重要。

  5. 高级无锁/无等待算法普及: 随着 VarHandle 的成熟和开发者对无锁编程理解的深入,更复杂、性能更优的无锁(Lock-Free)甚至无等待(Wait-Free)数据结构有望得到更广泛的应用。

结论:

CAS,这条源自硬件的原子指令,通过 JNI 的桥梁被 Java 所驾驭,并经由 AtomicXxxVarHandle 和 AQS 等精妙设计,成为了构建高性能、高并发 Java 应用的基石。它不仅是无锁编程的核心,更是隐藏在 ReentrantLock 等高级锁内部的动力引擎。深入理解 CAS 的硬件本质、JNI 实现路径、强大能力(无锁、高性能)与固有局限(ABA、高竞争开销),以及其优化手段(LongAdder)和演进方向(VarHandle),是每一位追求卓越的 Java 开发者的必修课。在并发编程的世界里,明智地选择 CAS 或锁,取决于对“竞争”二字的深刻洞察和务实权衡。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/87160.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/87160.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/87160.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【UnityAssetBundle】AssetBundle打包

AssetBundle生成AB包资源文件方式: Unity编辑器开发,自定义打包工具;官方提供好的打包工具,Asset Bundle Browser 打包 选择一个资源,new一个压缩包名称或选择一个压缩包名称 点击Window->AssetBundle Browser&…

Hush Puppies大中华区鞋类业务移交品牌方继续经营

据悉,随着百丽集团运营的暇步士(Hush Puppies)大中华区鞋类授权的到期,暇步士(Hush Puppies)鞋类业务已开始运营权移交。其中线上渠道授权于2025年6月30日正式到期,线下渠道将于2025年12月31日前…

解释LLM怎么预测下一个词语的

解释LLM怎么预测下一个词语的 通过上文词的向量进行映射 在Transformer架构的大语言模型(如GPT系列、BERT等)中,词语会先被转化为词向量。在预测下一个词时,模型会基于之前所有词的向量表示(并非仅仅上一个词,但上一个词的向量是重要信息来源之一)进行计算。 以GPT-2…

DAY 49 CBAM注意力

目录 DAY 49 CBAM注意力1.通道注意力模块复习2.空间注意力模块3.CBAM的定义作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 DAY 49 CBAM注意力 1.通道注意力模块复习 2.空间注意力模块 3.CBAM的定义 import torch import torch.nn …

【网络】Linux 内核优化实战 - net.ipv4.conf.all.rp_filter

目录 net.ipv4.conf.all.rp_filter 参数详解一、参数基本概念二、参数取值及含义三、反向路径过滤的工作原理四、配置示例与注意事项五、与其他参数的关联六、总结 net.ipv4.conf.all.rp_filter 参数详解 一、参数基本概念 net.ipv4.conf.all.rp_filter 是 Linux 内核中用于控…

ElementUI el-select多选下拉框,回显数据后无法重新选择和修改

问题 ElementUI el-select多选下拉框,回显数据后无法重新选择和修改,点击选择和删除都没有反应,页面也没有报错 方案一 网上搜出来的基本上都是这个解决办法,但是我设置后没有生效,还是无法选择和修改 原因 下拉框数…

计算机视觉的新浪潮:扩散模型(Diffusion Models)技术剖析与应用前景

近年来,扩散模型(Diffusion Models, DMs)迅速崛起,成为计算机视觉领域最令人瞩目的生成模型之一。从生成高质量图像到风格迁移、图像修复,再到文本驱动图像生成(如 DALLE 2、Stable Diffusion、Midjourney&…

「Java流程控制」跳转语句

今天来聊聊Java里的两个重要跳转语句——break和continue。它们就像马路上的交通信号灯,能够控制程序执行的流向。 break和continue break和continue在循环中的作用,特别像快递分拣中心的工作场景: break:就像发现一个破损包裹,直接停止当前分拣流程,把它扔进异常品处理…

R1-Searcher使用强化学习增强语言模型解决问题的搜索能力

R1-Searcher:Incentivizing the Search Capability in LLMs via Reinforcement Learning 2025.3 https://github.com/RUCAIBox/R1-Searcher 针对的问题: 现有大型推理模型在时间敏感或知识密集型问题上通常仅使用模型内部知识,导致回答不准…

C++中的虚函数与纯虚函数

文章目录 虚函数 (Virtual Function)纯虚函数 (Pure Virtual Function)主要区别实际应用示例 C中的虚函数和纯虚函数是实现多态性的重要机制。 虚函数 (Virtual Function) 虚函数是在基类中用virtual关键字声明的函数,它允许派生类重写(override)该函数的实现。当…

(LeetCode 每日一题) 3330. 找到初始输入字符串 I (字符串)

题目:3330. 找到初始输入字符串 I 思路:字符串,时间复杂度0(n)。 默认没有输错的情况ans1,而输错的情况,只会出现在连续相等字符串,假设这段字符串长度为ct,那么可能的情况为ct-1。累计这些和到…

Deep semi-supervised learning for medical image segmentation: A review

概述 医学图像分割的重要性:它是计算机辅助诊断(CAD)的关键部分,能帮助医生定位病变、评估治疗效果,减轻医生工作量。 深度学习技术的应用:U-Net等网络在医学图像分割中表现优异,近期大型视觉语…

[云上玩转Qwen3系列之四]PAI-LangStudio x AI搜索开放平台 x ElasticSearch: 构建AI Search RAG全栈应用

本文详细介绍了如何使用 PAI-LangStudio 和 Qwen3 构建基于AI搜索开放平台 x ElasticSearch 的 AI Search RAG 智能检索应用。该应用通过使用 AI 搜索开放平台、ElasticSearch 全文检索向量检索引擎的混合检索技术配合阿里云最新发布的 Qwen3 推理模型编排在一个 Agentic Workf…

前端请求浏览器提示net::ERR_UNSAFE_PORT的解决方案

起因 项目中后端给到了6666端口的服务地址, 随即前端项目访问中浏览器报错如下: 不安全端口在主流浏览器(Chrome/Firefox/Edge/Safari)中会被拦截,触发浏览器Network的status列显示 net::ERR_UNSAFE_PORT 错误, 以下是常见的不安全端口一览…

【Bluedroid】蓝牙设备管理器初始化全流程深度解析(BTA_dm_on_hw_on)

本文全面剖析Android蓝牙设备管理器在硬件启动时的初始化流程,涵盖控制块创建、服务发现启动、设备类配置、安全密钥加载、超时参数设置等核心环节。通过分析从底层硬件交互到上层服务注册的全链路调用,揭示蓝牙系统从硬件就绪到功能可用的完整启动机制&…

大语言模型:是逐字生成还是一次多词?

大语言模型(LLM)既可以按顺序逐个生成单词(token),也能实现一次生成多个 token 核心差异源于解码策略与模型架构设计 一、常规“逐个生成”模式(基础逻辑) 多数入门级演示或简单文本生成中,LLM 会默认按 “生成一个 token → 拼接回输入 → 再生成下一个” 的流程,…

通俗易懂的LangGraph图定义解析

LangGraph 是一个基于状态的工作流框架,它通过 节点(Nodes) 和 边(Edges) 的组合,构建出复杂的工作流逻辑。这种设计特别适合处理需要动态决策、循环、多步骤交互的场景(比如对话系统、智能代理…

K8s Pod调度基础——2

目录 一、Deployment ‌一、Deployment 原理‌ ‌二、核心特性‌ ‌三、意义与场景‌ ‌四、示例与逐行解释‌ ‌五、总结‌ StatefulSet ‌一、StatefulSet 原理‌ ‌二、核心特性‌ ‌三、意义与场景‌ ‌四、示例与逐行解释‌ ‌五、总结‌ 彼此的区别 一、本质…

Java 大视界 -- Java 大数据在智能医疗健康管理中的慢性病风险预测与个性化干预(330)

Java 大视界 -- Java 大数据在智能医疗健康管理中的慢性病风险预测与个性化干预(330) 引言:正文:一、Java 构建的医疗数据融合平台(多源数据安全打通)1.1 分布式医疗数据集成系统(符合 HIPAA 与…

beego打包发布到Centos系统及国产麒麟系统完整教程

1、先清除go缓存,用下面命令 go clean -cache go clean -modcache 2、更新库文件 go mod tidy 3、安装beego go install github.com/beego/bee/v2latest 4、查看bee版本 5、进行打包然后传到Centos和麒麟服务器如下代码 bee pack -be GOOSlinux -be GOARCHa…