Java 并發編程的 CAS(Compare and Swap)是什么?

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?的現代替代品。

    • 高級鎖與同步器:?ReentrantLock,?Semaphore,?CountDownLatch?等依賴于?AbstractQueuedSynchronizer (AQS)。AQS 的核心狀態變量?state?是一個?volatile int,其獲取和釋放操作高度依賴 CAS?來嘗試無鎖地修改狀態。CAS 是這些鎖實現內部快速路徑(fast-path)的核心機制。

三、CAS 有什么用?無鎖并發的引擎

  1. 實現無鎖(Lock-Free)算法:?允許線程在不使用阻塞鎖(如?synchronized?或?ReentrantLock.lock())的情況下安全地更新共享數據。這是其最核心的價值

  2. 構建高性能并發工具:

    • 原子類 (AtomicInteger,?AtomicLong,?AtomicReference):?提供線程安全的計數器、標志位、對象引用更新。

    • 無鎖數據結構:?ConcurrentLinkedQueue?(無鎖隊列),?ConcurrentHashMap?(JDK8+ 使用 CAS 優化桶操作)。

    • 高級鎖與同步器基礎 (AQS):?ReentrantLock,?ReentrantReadWriteLock,?Semaphore,?CountDownLatch?等內部狀態 (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?的靈活性:?提供更細粒度的內存排序控制(acquire,?release?等語義),有時能生成更優化的代碼。

六、使用 CAS 的注意點

  1. 警惕 ABA:?評估業務邏輯是否允許值“回頭”。如果不允許,必須使用帶版本戳或標記的原子引用。

  2. 權衡競爭強度:?低/中競爭是 CAS 的主場。高競爭時,優先考慮?LongAdder、鎖(synchronized,?ReentrantLock)或嘗試減少共享變量爭用(如數據分片)。

  3. 避免重復造輪子:?優先使用成熟的?java.util.concurrent?工具類 (AtomicXxx,?ConcurrentHashMap,?LongAdder)。自行實現無鎖算法風險高。

  4. 理解內存可見性:?CAS 操作本身具有?volatile?讀寫的內存語義,能保證變量的可見性。使用?VarHandle?時可更精確控制內存屏障。

  5. 平臺差異:?雖然 Java API 統一,但底層 CAS 性能在不同 CPU 上有差異(通常 JVM 會優化處理)。

七、典型使用場景

  1. 計數器/統計:?AtomicInteger,?AtomicLong,?LongAdder?(高并發計數首選)。

  2. 狀態標志位:?AtomicBoolean?或?volatile?+ CAS 更新。

  3. 構建無鎖數據結構:?ConcurrentLinkedQueue,?ConcurrentHashMap?(桶操作、樹結構調整)。

  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):?AtomicInteger,?AtomicReference?等提供易用的 CAS API。

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

  5. 高并發計數優化 (Java 8):?LongAdder,?DoubleAdder?解決?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),高競爭時也能公平有效地管理線程。ReentrantLock,?Semaphore?等都是基于 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…