【多線程】CAS詳解

目錄

  • 🌴什么是 CAS
    • 🌸CAS 偽代碼
  • 🎍CAS 是怎么實現的
  • 🍀CAS 有哪些應?
    • 🌸實現原子類
    • 🌸實現自旋鎖
  • 🌳CAS 的 ABA 問題
    • 🌸**什么是 ABA 問題**?
    • 🌸ABA 問題引來的 BUG
    • 🌸解決方案
  • ?相關面試題
  • 🚩總結

🌴什么是 CAS

CAS: 全稱Compare and swap字?意思:”?較并交換“,?個 CAS 涉及到以下操作:

我們假設內存中的原數據V,舊的預期值A,需要修改的新值B。

  1. ?較 A 與 V 是否相等。(?較)
  2. 如果?較相等,將 B 寫? V。(交換)
  3. 返回操作是否成功。

🌸CAS 偽代碼

下?寫的代碼不是原?的, 真實的 CAS 是?個原?的硬件指令完成的. 這個偽代碼只是輔助理解 CAS的?作流程.

boolean CAS(address, expectValue, swapValue) {if (&address == expectedValue) {&address = swapValue;return true;}return false;
}

下面看兩種典型的不是原子性的代碼:

  1. check and set (if 判定然后設定值) [上?的 CAS 偽代碼就是這種形式]
  2. read and update (i++) [之前我們講線程安全的代碼例?是這種形式]

CAS原子性的作用

當多個線程同時對某個資源進?CAS操作,只能有?個線程操作成功,但是并不會阻塞其他線程,其他
線程只會收到操作失敗的信號

所以

CAS 可以視為是?種樂觀鎖. (或者可以理解成 CAS 是樂觀鎖的?種實現?式)

🎍CAS 是怎么實現的

針對不同的操作系統,JVM ?到了不同的 CAS 實現原理,簡單來講:

  • java 的 CAS 利?的的是 unsafe 這個類提供的 CAS 操作;
  • unsafe 的 CAS 依賴了的是 jvm 針對不同的操作系統實現的 Atomic::cmpxchg;
  • Atomic::cmpxchg 的實現使?了匯編的 CAS 操作,并使? cpu 硬件提供的 lock 機制保證其原?
    性。

簡??之,是因為硬件予以了?持,軟件層?才能做到

🍀CAS 有哪些應?

🌸實現原子類

標準庫中提供了 java.util.concurrent.atomic 包, ??的類都是基于這種?式來實現的.
典型的就是 AtomicInteger 類. 其中的 getAndIncrement 相當于 i++ 操作.

AtomicInteger atomicInteger = new AtomicInteger(0);
// 相當于 i++
atomicInteger.getAndIncrement();

偽代碼實現:

class AtomicInteger {private int value;public int getAndIncrement() {int oldValue = value;while ( CAS(value, oldValue, oldValue+1) != true) {oldValue = value;}return oldValue;}}

假設兩個線程同時調? getAndIncrement

  1. 兩個線程都讀取 value 的值到 oldValue 中. (oldValue 是?個局部變量, 在棧上. 每個線程有??的
    棧)
    在這里插入圖片描述
  2. 線程1 先執? CAS 操作. 由于 oldValue 和 value 的值相同, 直接進?對 value 賦值.

注意:
? CAS 是直接讀寫內存的, ?不是操作寄存器.
? CAS 的讀內存, ?較, 寫內存操作是?條硬件指令, 是原?的.

在這里插入圖片描述
3. 線程2 再執? CAS 操作, 第?次 CAS 的時候發現 oldValue 和 value 不相等, 不能進?賦值. 因此需要進?循環.

在循環?重新讀取 value 的值賦給 oldValue
在這里插入圖片描述
4. 線程2 接下來第?次執? CAS, 此時 oldValue 和 value 相同, 于是直接執?賦值操作.

在這里插入圖片描述
5. 線程1 和 線程2 返回各?的 oldValue 的值即可.

通過形如上述代碼就可以實現?個原?類. 不需要使?重量級鎖, 就可以?效的完成多線程的?增操作.

本來 check and set 這樣的操作在代碼?度不是原?的. 但是在硬件層?上可以讓?條指令完成這個
操作, 也就變成原?的了.

🌸實現自旋鎖

基于 CAS 實現更靈活的鎖, 獲取到更多的控制權.

?旋鎖偽代碼:

public class SpinLock {private Thread owner = null;public void lock(){// 通過 CAS 看當前鎖是否被某個線程持有. // 如果這個鎖已經被別的線程持有, 那么就?旋等待. // 如果這個鎖沒有被別的線程持有, 那么就把 owner 設為當前嘗試加鎖的線程. while(!CAS(this.owner, null, Thread.currentThread())){}}public void unlock (){this.owner = null;}
}

🌳CAS 的 ABA 問題

🌸什么是 ABA 問題

通過下面的例子理解ABA問題:

假設存在兩個線程 t1 和 t2. 有?個共享變量 num, 初始值為 A.
接下來, 線程 t1 想使? CAS 把 num 值改成 Z, 那么就需要

  • 先讀取 num 的值, 記錄到 oldNum 變量中.
  • 使? CAS 判定當前 num 的值是否為 A, 如果為 A, 就修改成 Z.

但是, 在 t1 執?這兩個操作之間, t2 線程可能把 num 的值從 A 改成了 B, ?從 B 改成了 A

線程 t1 的 CAS 是期望 num 不變就修改. 但是 num 的值已經被 t2 給改了. 只不過?改成 A 了. 這個時
候 t1 究竟是否要更新 num 的值為 Z 呢?

到這?步, t1 線程?法區分當前這個變量始終是 A, 還是經歷了?個變化過程.

在這里插入圖片描述
這就好?, 我們買?個?機, ?法判定這個?機是剛出?的新?機, 還是別??舊了, ?翻新過的?機.

🌸ABA 問題引來的 BUG

?部分的情況下, t2 線程這樣的?個反復橫跳改動, 對于 t1 是否修改 num 是沒有影響的. 但是不排除?些特殊情況.

假設 滑稽?哥 有 100 存款. 滑稽想從 ATM 取 50 塊錢. 取款機創建了兩個線程, 并發的來執? -50 操 作.
我們期望?個線程執? -50 成功, 另?個線程 -50 失敗. 如果使? CAS 的?式來完成這個扣款過程就可能出現問題.

正常的過程

  1. 存款 100. 線程1 獲取到當前存款值為 100, 期望更新為 50; 線程2 獲取到當前存款值為 100, 期望更
    新為 50.
  2. 線程1 執?扣款成功, 存款被改成 50. 線程2 阻塞等待中.
  3. 輪到線程2 執?了, 發現當前存款為 50, 和之前讀到的 100 不相同, 執?失敗.

異常的過程

  1. 存款 100. 線程1 獲取到當前存款值為 100, 期望更新為 50; 線程2 獲取到當前存款值為 100, 期望更
    新為 50.
  2. 線程1 執?扣款成功, 存款被改成 50. 線程2 阻塞等待中.
  3. 在線程2 執?之前, 滑稽的朋友正好給滑稽轉賬 50, 賬?余額變成 100 !!
  4. 輪到線程2 執?了, 發現當前存款為 100, 和之前讀到的 100 相同, 再次執?扣款操作

這個時候, 扣款操作被執?了兩次!!! 都是 ABA 問題搞的?!!

🌸解決方案

給要修改的值, 引?版本號. 在 CAS ?較數據當前值和舊值的同時, 也要?較版本號是否符合預期.

? CAS 操作在讀取舊值的同時, 也要讀取版本號.
? 真正修改的時候,

  • 如果當前版本號和讀到的版本號相同, 則修改數據, 并把版本號 + 1.
  • 如果當前版本號?于讀到的版本號. 就操作失敗(認為數據已經被修改過了).

這就好?, 判定這個?機是否是翻新機, 那么就需要收集每個?機的數據, 第?次掛在電商?站上的?
機記為版本1, 以后每次這個?機出現在電商?站上, 就把版本號進?遞增. 這樣如果買家不在意這是翻
新機, 就買. 如果買家在意, 就可以直接略過.

對?理解上?的轉賬例子

假設 滑稽?哥 有 100 存款. 滑稽想從 ATM 取 50 塊錢. 取款機創建了兩個線程, 并發的來執? -50 操 作.
我們期望?個線程執? -50 成功, 另?個線程 -50 失敗. 為了解決 ABA 問題, 給余額搭配?個版本號, 初始設為 1.

  1. 存款 100. 線程1 獲取到 存款值為 100, 版本號為 1, 期望更新為 50; 線程2 獲取到存款值為 100, 版本
    號為 1, 期望更新為 50.
  2. 線程1 執?扣款成功, 存款被改成 50, 版本號改為2. 線程2 阻塞等待中.
  3. 在線程2 執?之前, 滑稽的朋友正好給滑稽轉賬 50, 賬?余額變成 100, 版本號變成3.
  4. 輪到線程2 執?了, 發現當前存款為 100, 和之前讀到的 100 相同, 但是當前版本號為 3, 之前讀到的版本號為 1, 版本?于當前版本, 認為操作失敗.

在 Java 標準庫中提供了 AtomicStampedReference 類. 這個類可以對某個類進?包裝, 在內
部就提供了上?描述的版本管理功能.

關于 AtomicStampedReference 的具體?法此處不再展開. 有需要的同學??查找?檔了
解使??法即可.

?相關面試題

  1. 講解下你??理解的 CAS 機制

全稱 Compare and swap, 即 “?較并交換”. 相當于通過?個原?的操作, 同時完成 “讀取內存, ?較是
否相等, 修改內存” 這三個步驟. 本質上需要 CPU 指令的?撐

2.ABA問題怎么解決?

給要修改的數據引?版本號. 在 CAS ?較數據當前值和舊值的同時, 也要?較版本號是否符合預期. 如
果發現當前版本號和之前讀到的版本號?致, 就真正執?修改操作, 并讓版本號?增; 如果發現當前版
本號?之前讀到的版本號?, 就認為操作失敗

🚩總結

關于《【多線程】CAS詳解》就講解到這兒,感謝大家的支持,歡迎各位留言交流以及批評指正,如果文章對您有幫助或者覺得作者寫的還不錯可以點一下關注,點贊,收藏支持一下!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/713818.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/713818.shtml
英文地址,請注明出處:http://en.pswp.cn/news/713818.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【C++】核心編程--函數高級

文章目錄 1. 函數的默認參數2. 函數占位參數3. 函數重載4. 注意事項 1. 函數的默認參數 在C中&#xff0c;函數的形參列表中的形參是可以有默認值的 //語法&#xff1a; 返回值類型 函數名 (參數 默認值){} #include<iostream> using namespace std; //函數默認參數 //如…

異常值檢測-3σ法提交 代碼注釋

背景信息里面都給了相應的答案&#xff0c;但我們可以多了解一下代碼的含義&#xff0c;而不是簡單的復制粘貼 import pandas as pd import matplotlib.pyplot as plt from scipy import stats import numpy as npdata pd.read_csv("src/death.csv", index_colUnna…

ASPICE實操中的那點事兒-如何避免重復性測試

寫在前面 ASPICE理解起來容易&#xff0c;畢竟是有條有理的。但實操起來&#xff0c;尤其是把ASPICE各過程域做全的時候&#xff0c;會遇到各種各樣的問題&#xff08;不是技術問題有多難&#xff0c;而是該如何做選擇&#xff0c;如何既能符合ASPICE要求&#xff0c;保證過程質…

智慧城市建設的新里程碑:公共服務電子支付大屏

隨著科技的飛速發展&#xff0c;我們的生活正在經歷前所未有的變革。電子支付的出現&#xff0c;無疑是這場變革中的一大亮點&#xff0c;它不僅改變了我們日常的支付方式&#xff0c;更成為智慧城市建設的重要一環&#xff0c;為公眾提供了更加便捷、高效的服務體驗。 在以前&…

python SHP2COCO

1. 將shp的標簽數據轉成coco # -*- coding: utf-8 -*- import os, json import cv2 from osgeo import gdal import numpy as np from osgeo import ogr, gdal, osr from shapely.geometry import box, shape from shapely.geometry.polygon import Polygon import collection…

Flutter 的狀態管理

狀態提升&#xff08;Lifting-state-up&#xff09; 把子組件的狀態&#xff0c;提升到上級組件中&#xff0c;從而實現在多個組件之間共享和同步數據的效果 以 flutter counter demo&#xff0c;那個按按鈕1 的來說&#xff0c;現在的 count 是幾&#xff0c;不是存在頁面顯…

政府采購標書制作的要點解析

導語&#xff1a;政府采購是政府為滿足公共利益&#xff0c;按照法定程序和標準&#xff0c;通過招標、競爭性談判等方式&#xff0c;購買商品、工程和服務的行為。標書作為政府采購活動中的重要文件&#xff0c;其制作質量直接影響到項目的順利進行。本文將圍繞政府采購標書制…

二路歸并排序的算法設計和復雜度分析and周記

數據結構實驗報告 實驗目的: 通過本次實驗&#xff0c;了解算法復雜度的分析方法&#xff0c;掌握遞歸算法時間復雜度的遞推計算過程。 實驗內容&#xff1a; 二路歸并排序的算法設計和復雜度分析 實驗過程&#xff1a; 1.算法設計 第一步&#xff0c;首先要將數組進行…

【網站項目】314學生二手書籍交易平臺

&#x1f64a;作者簡介&#xff1a;擁有多年開發工作經驗&#xff0c;分享技術代碼幫助學生學習&#xff0c;獨立完成自己的項目或者畢業設計。 代碼可以私聊博主獲取。&#x1f339;贈送計算機畢業設計600個選題excel文件&#xff0c;幫助大學選題。贈送開題報告模板&#xff…

關于游戲公司組織架構的小討論

過完年剛剛上班沒幾天&#xff0c;就有一件比較搞笑的事情&#xff0c;可以和大家分享一下。 ??某一天我們在公司的會議室開會&#xff0c;發現有非常多蚊子&#xff0c;于是找行政問能不能找專業人士來滅蚊。行政的答復是&#xff0c;專業滅蚊是有固定時間的&#xff0c;還要…

JVM相關面試題(2024大廠高頻面試題系列)

一、JVM的組成 1、JVM由哪些部分組成&#xff0c;運行流程是什么&#xff1f; 回答&#xff1a;在JVM中共有四大部分&#xff0c;分別是Class Loader&#xff08;類加載器&#xff09;、Runtime Data Area&#xff08;運行時數據區&#xff0c;內存分區&#xff09;、Execut…

MyBatis的補充用法

說明&#xff1a;之前介紹過MyBatis的用法&#xff0c;像 用注解和Mapper.xml操作數據庫、在Mapper.xml里寫動態SQL。最近在一次用MyBatis批量更新數據庫對象的場景中&#xff0c;意識到對MyBatis的一些標簽用法不太熟悉&#xff0c;所以去 MyBatis官網 看了一些文檔&#xff0…

php httpfs鏈接hdfs

一.代碼&#xff08;有bug&#xff09; GitHub - michaelbutler/php-WebHDFS: A PHP client for WebHDFS 二.調用代碼 1.代碼1.代碼 require_once(../webhdfs/src/org/apache/hadoop/WebHDFS.php);require_once(../webhdfs/src/org/apache/hadoop/tools/Curl.php); require_o…

什么是人才儲備?如何做人才儲備?

很多小伙伴都會有企業面試被拒的情況&#xff0c;然后HR會告訴你&#xff0c;雖然沒有錄用你&#xff0c;但是你進入了他們的人才儲備庫&#xff0c;那么這個儲備庫有什么作用和特點呢&#xff1f;我們如何應用人才測評系統完善人才儲備庫呢&#xff1f; 人才儲備一般有以下三…

Python打發無聊時光:12.用PyQt實現簡易的心電起搏器界面

第一步&#xff1a;裝PyQt庫 pip install PyQt5 第二步&#xff1a;復制代碼 import sys from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QVBoxLayout,QWidget, QLabel, QProgressBar, QSlider, QLineEdit, QHBoxLayout) from PyQt5.QtCore import …

軟件分層(數據結構/軟件邏輯上分層+舉例),相連節點的概念+如何相連,為什么是層狀結構(軟件分層,網絡協議分層+梳理協議順序),協議分層(打電話例子)

目錄 軟件分層 介紹 舉例 類的繼承 虛擬文件系統 線程接口封裝 虛擬地址空間 總結 為什么是層狀的 軟件分層 網絡協議 原因 梳理協議順序 相連節點 協議分層 引入 示例 實際上 邏輯上 制定出協議 軟件分層 介紹 通過將軟件系統劃分為不同的層次,每一層都有…

uniApp 調整小程序 單個/全部界面橫屏展示效果

我們打開uni項目 小程序端運行 默認是豎著的一個效果 我們打開項目的 pages.json 給需要橫屏的界面 的 style 屬性 加上 "mp-weixin": {"pageOrientation": "landscape" }界面就橫屏了 如果是要所有界面都橫屏的話 就直接在pages.json 的 gl…

Ps:海綿工具

海綿工具 Sponge Tool可用于調整圖像中特定區域的飽和度&#xff0c;常用于增加或減少顏色的飽和度。 快捷鍵&#xff1a;O 在特別的灰度圖像上&#xff0c;則可用于調整對比度&#xff0c;這可以開發出更多的創意技巧。 ◆ ◆ ◆ 常用操作方法與技巧 1、海綿工具主要用于調整…

源碼解析篇 | YOLOv8官方源碼項目目錄結構解析

前言&#xff1a;Hello大家好&#xff0c;我是小哥談。YOLOv8是一種目標檢測算法&#xff0c;它是YOLO&#xff08;You Only Look Once&#xff09;系列算法的第8個版本。YOLOv8相比于之前的版本&#xff0c;在檢測精度和速度上都有所提升&#xff0c;它在各種場景下都表現出色…

Git源碼管理

參考視頻&#xff1a;16-git的日志以及版本管理_嗶哩嗶哩_bilibili 參考博客&#xff1a;Git && Docker 學習筆記-CSDN博客 目錄 簡介 個人操作初始化 初始化git目錄 查看生成的git目錄文件 配置git工作目錄的用戶信息 查看工作區的狀態&#xff0c;生成文件的…