JAVA中的CAS

来源:https://www.cnblogs.com/Shuuichi/archive/2019/03/24/10590710.html
-Advertisement-
Play Games

一、CAS概念與原理 CAS,全稱Compare And Swap(比較與交換),解決多線程並行情況下使用鎖造成性能損耗的一種機制。 實現思想 CAS(V, A, B),V為記憶體地址、A為預期原值,B為新值。如果記憶體地址的值與預期原值相匹配,那麼將該位置值更新為新值。否則,說明已經被其他線程更新,處 ...


一、CAS概念與原理

  CAS,全稱Compare And Swap(比較與交換),解決多線程並行情況下使用鎖造成性能損耗的一種機制。

  實現思想 CAS(V, A, B),V為記憶體地址、A為預期原值,B為新值。如果記憶體地址的值與預期原值相匹配,那麼將該位置值更新為新值。否則,說明已經被其他線程更新,處理器不做任何操作;無論哪種情況,它都會在 CAS 指令之前返回該位置的值。而我們可以使用自旋鎖,迴圈CAS,重新讀取該變數再嘗試再次修改該變數,也可以放棄操作。

  CAS操作由處理器提供支持,是一種原語。原語是操作系統或電腦網路用語範疇。是由若幹條指令組成的,用於完成一定功能的一個過程,具有不可分割性,即原語的執行必須是連續的,在執行過程中不允許被中斷。如 Intel 處理器,比較並交換通過指令的 cmpxchg 系列實現。處理器相關指令不做過多介紹,有興趣的可自行查閱資料。

 

二、JDK1.8 中的CAS

  Unsafe類,在sun.misc包下,不屬於Java標準。Unsafe類提供一系列增加Java語言能力的操作,如記憶體管理、操作類/對象/變數、多線程同步等。其中與CAS相關的方法有以下幾個:

//var1為CAS操作的對象,offset為var1某個屬性的地址偏移值,expected為期望值,var2為要設置的值,利用JNI來完成CPU指令的操作
public final native boolean compareAndSwapObject(Object var1, long offset, Object expected, Object var2);
public final native boolean compareAndSwapInt(Object var1, long offset, int expected, int var2);
public final native boolean compareAndSwapLong(Object var1, long offset, long expected, long var2);

  

    /** 如果CAS成功,return oldValue, oldValue =  oldValue + addValue
     *  如果CAS失敗,自旋,一直運行,直到成功為止
     */
    public final Xxx getAndAddXxx(Object var1, long offset, long addValue) {
        int oldValue;
        do {
            oldValue = this.getIntVolatile(var1, offset);
        } while(!this.compareAndSwapInt(var1, offset, oldValue, oldValue + addValue));

        return oldValue;
    }

    /** 如果CAS成功,return oldValue, oldValue =  newValue
     *  如果CAS失敗,自旋,一直運行,直到成功為止
     */
    public final Xxx getAndSetXxx(Object var1, long offset, Object newValue) {
        int oldValue;
        do {
            oldValue = this.getXxxVolatile(var1, offset);
        } while(!this.compareAndSwapXxx(var1, offset, oldValue, newValue));

        return oldValue;
    }

  一般不建議使用Unsafe類,除非對它有很深入的瞭解。

  

  java.util.concurrent包中大量使用了CAS原理,如AtomicInteger類,都是調用上面幾個Unsafe方法保證多線程數據的正確性

  以下是AtomicInteger的CAS操作相關源碼

  1 public class AtomicInteger extends Number implements java.io.Serializable {
  2     private static final long serialVersionUID = 6214790243416807050L;
  3 
  4     // setup to use Unsafe.compareAndSwapInt for updates
  5     // Unsafe類,提供一系列增強Java的功能,如記憶體管理、操作類/對象/變數、多線程同步等。不建議開發者調用
  6     private static final Unsafe unsafe = Unsafe.getUnsafe();
  7     // 獲取對象某個屬性的地址偏移值
  8     private static final long valueOffset;
  9 
 10     static {
 11         try {
 12             // value相對“起始地址”的偏移量
 13             valueOffset = unsafe.objectFieldOffset
 14                     (AtomicInteger.class.getDeclaredField("value"));
 15         } catch (Exception ex) { throw new Error(ex); }
 16     }
 17 
 18     // value值, volatile修飾,保證不同線程間的可見性
 19     private volatile int value;
 20     public AtomicInteger(int initialValue) { value = initialValue; }
 21     public AtomicInteger() {}
 22 
 23     public final int get() { return value; }
 24     public final void set(int newValue) { value = newValue; }
 25 
 26     /**
 27      * Eventually sets to the given value.
 28      *
 29      * @param newValue the new value
 30      * @since 1.6
 31      */
 32     public final void lazySet(int newValue) {
 33         //有序或者有延遲的putIntVolatile方法
 34         unsafe.putOrderedInt(this, valueOffset, newValue);
 35     }
 36 
 37     /**
 38      * Atomically sets to the given value and returns the old value.
 39      * @param newValue the new value
 40      * @return the previous value
 41      */
 42     public final int getAndSet(int newValue) {
 43         return unsafe.getAndSetInt(this, valueOffset, newValue);
 44     }
 45 
 46     /**
 47      * Atomically sets the value to the given updated value
 48      * if the current value {@code ==} the expected value.
 49      * @param expect the expected value
 50      * @param update the new value
 51      * @return {@code true} if successful. False return indicates that
 52      * the actual value was not equal to the expected value.
 53      */
 54     public final boolean compareAndSet(int expect, int update) {
 55         // JNI調用,實現CAS
 56         return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
 57     }
 58 
 59     /**
 60      * i++ 操作
 61      * Atomically increments by one the current value.
 62      * @return the previous value
 63      */
 64     public final int getAndIncrement() {
 65         return unsafe.getAndAddInt(this, valueOffset, 1);
 66     }
 67 
 68     /**
 69      * i-- 操作
 70      * Atomically decrements by one the current value.
 71      * @return the previous value
 72      */
 73     public final int getAndDecrement() {
 74         return unsafe.getAndAddInt(this, valueOffset, -1);
 75     }
 76 
 77     /**
 78      * return i, i = i + n 操作
 79      * Atomically adds the given value to the current value.
 80      * @param delta the value to add
 81      * @return the previous value
 82      */
 83     public final int getAndAdd(int delta) {
 84         return unsafe.getAndAddInt(this, valueOffset, delta);
 85     }
 86 
 87     /**
 88      * ++i 操作
 89      * Atomically increments by one the current value.
 90      * @return the updated value
 91      */
 92     public final int incrementAndGet() {
 93         return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
 94     }
 95 
 96     /**
 97      * --i 操作
 98      * Atomically decrements by one the current value.
 99      * @return the updated value
100      */
101     public final int decrementAndGet() {
102         return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
103     }
104 
105     /**
106      * i = i + n ,return i操作
107      * Atomically adds the given value to the current value.
108      * @param delta the value to add
109      * @return the updated value
110      */
111     public final int addAndGet(int delta) {
112         return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
113     }
114     // 其餘函數,略...
115

  

三、CAS缺點

  CAS有幾個缺點:

  1、ABA問題。當第一個線程執行CAS操作,尚未修改為新值之前,記憶體中的值已經被其他線程連續修改了兩次,使得變數值經歷 A -> B -> A的過程。

  解決方案:添加版本號作為標識,每次修改變數值時,對應增加版本號; 做CAS操作前需要校驗版本號。JDK1.5之後,新增AtomicStampedReference類來處理這種情況。

  2、迴圈時間長開銷大。如果有很多個線程併發,CAS自旋可能會長時間不成功,會增大CPU的執行開銷。

  3、只能對一個變數進原子操作。JDK1.5之後,新增AtomicReference類來處理這種情況,可以將多個變數放到一個對象中。

  

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 超級開心啊!!!!!!!!!!!!! win10 打開cmd Installing with get-pip.py To install pip, securely download get-pip.py. [1]: curl https://bootstrap.pypa.io/get-pip.py ...
  • 一,File類:文件的創建和刪除 1.File(String pathname):pathname是指路徑名稱。用法 File file = new File("d:/1.txt "); 2.File(String parent, String child):parent是父路徑字元串,child是 ...
  • 許多公司網站被黑被進犯,首要牽扯到的便是網站的開發言語,包含了代碼言語,以及資料庫言語,現在大多數網站都是運用的PHP,JAVA,.net言語開發,資料庫運用的是mysql,oracle等資料庫,那麼網站被進犯了該怎樣辦?運營一個網站,總被進犯是時有發生的,特別一些公司網站,以及個人建站,都是沒有專 ...
  • 新聞 "Amazon.Lambda.RuntimeSupport發佈" "Forge 3.0架構" "Blazor 0.9.0試驗版發佈" "通過微軟游戲棧實現更多應用" "介紹ASP.NET Core中的gRPC" "Mac上的Visual Studio 2019 8.0版本預覽4" "FlexS ...
  • 說明 操作系統:Windows 10 Python 版本:3.7x 虛擬環境管理器:virtualenv 代碼編輯器:VS Code 環境搭建 打開 執行下述操作 Hello World 在 目錄下創建一個 __init__.py ,示例代碼如下所示: 在 目錄下創建一個 manage.py 文件, ...
  • 前言:由於對面向對象思想認識的不夠深刻,所以這一單元的作業寫的是非常不oo的,從代碼結構來看,結構也顯得有些混亂,,沒有一個清晰的設計。 作業分析 第一次作業 反思 1. 輸入 對於三次的作業其實大部分的難點就是在判斷輸入的合法性上,對於第一次作用來說,最初的想法還是用一整個正則表達式來判斷輸入,但 ...
  • ~~「前言」為防止在某巨巨的毒瘤idea 紫荊花之“店" 面前一臉懵,特滾來補此題。~~ ~~我還是太naive了。~~ 「題義」給出一棵單點度數很小的無根帶邊權、點權的樹,每次詢問在所有點權在\[l,r\]的點到c的距離之和。 「分析」考慮建立點分樹,分治結構每個點都儲存對應分治範圍(簡稱範圍)內 ...
  • 一、環境需求 二、怎樣使用 三、本地化 3.1擴展卡爾曼濾波本地化 3.2無損卡爾曼濾波本地化 3.3粒子濾波本地化 3.4直方圖濾波本地化 四、映射 4.1高斯網格映射 4.2光線投射網格映射 4.3k均值物體聚類 4.4圓形擬合物體形狀識別 五、SLAM 5.1迭代最近點匹配 5.2EKF SL ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...