CAS原理分析及ABA問題詳解

什么是CAS

CASCompare And Swap的縮寫,翻譯成中文就是比較并交換,其作用是讓CPU比較內存中某個值是否和預期的值相同,如果相同則將這個值更新為新值,不相同則不做更新,也就是CAS是原子性的操作(讀和寫兩者同時具有原子性),其實現方式是通過借助C/C++調用CPU指令完成的,所以效率很高。
CAS的原理很簡單,這里使用一段Java代碼來描述

public boolean compareAndSwap(int value, int expect, int update) {
//        如果內存中的值value和期望值expect一樣 則將值更新為新值updateif (value == expect) {value = update;return true;} else {return false;}
}
復制代碼

大致過程是將內存中的值、我們的期望值、新值交給CPU進行運算,如果內存中的值和我們的期望值相同則將值更新為新值,否則不做任何操作。這個過程是在CPU中完成的,這里不好描述CPU的工作過程,就拿Java代碼來描述了。

Unsafe源碼分析

Java是在Unsafe(sun.misc.Unsafe)類實現CAS的操作,而我們知道Java是無法直接訪問操作系統底層的API的(原因是Java的跨平臺性限制了Java不能和操作系統耦合),所以Java并沒有在Unsafe類直接實現CAS的操作,而是通過**JDI(Java Native Interface)**本地調用C/C++語言來實現CAS操作的。
Unsafe有很多個CAS操作的相關方法,這里舉例幾個

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
復制代碼

我們拿public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);進行分析,這個方法是比較內存中的一個值(整型)和我們的期望值(var4)是否一樣,如果一樣則將內存中的這個值更新為var5,參數中的var1是值所在的對象,var2是值在對象(var1)中的內存偏移量,參數var1和參數var2是為了定位出值所在內存的地址

Unsafe.java在這里發揮的作用有:

  1. 將對象引用、值在對象中的偏移量、期望的值和欲更新的新值傳遞給Unsafe.cpp
  2. 如果值更新成功則返回true給開發者,沒有更新則返回false

Unsafe.cpp在這里發揮的作用有:

  1. 接受從Unsafe傳遞過來的對象引用、偏移量、期望的值和欲更新的新值,根據對象引用和偏移量計算出值的地址,然后將值的地址、期望的值、欲更新的新值傳遞給CPU
  2. 如果值更新成功則返回trueUnsafe.java,沒有更新則返回false

CPU在這里發揮的作用:

  1. 接受從Unsafe.cpp傳遞過來的地址、期望的值和欲更新的新值,執行指令cmpxchg,比較地址中的值是否和期望的值一樣,一樣則將值更新為新的值,不一樣則不做任何操作
  2. 將操作結果返回給Unsafe.cpp

CAS的缺點

CAS雖然高效的實現了原子性操作,但是也存在一些缺點,主要表現在以下三個方面。

ABA問題

在多線程場景下CAS會出現ABA問題,關于ABA問題這里簡單科普下,例如有2個線程同時對同一個值(初始值為A)進行CAS操作,這三個線程如下

  1. 線程1,期望值為A,欲更新的值為B
  2. 線程2,期望值為A,欲更新的值為B

線程1搶先獲得CPU時間片,而線程2因為其他原因阻塞了,線程1取值與期望的A值比較,發現相等然后將值更新為B,然后這個時候出現了線程3,期望值為B,欲更新的值為A,線程3取值與期望的值B比較,發現相等則將值更新為A,此時線程2從阻塞中恢復,并且獲得了CPU時間片,這時候線程2取值與期望的值A比較,發現相等則將值更新為B,雖然線程2也完成了操作,但是線程2并不知道值已經經過了A->B->A的變化過程。

ABA問題帶來的危害
小明在提款機,提取了50元,因為提款機問題,有兩個線程,同時把余額從100變為50
線程1(提款機):獲取當前值100,期望更新為50,
線程2(提款機):獲取當前值100,期望更新為50,
線程1成功執行,線程2某種原因block了,這時,某人給小明匯款50
線程3(默認):獲取當前值50,期望更新為100,
這時候線程3成功執行,余額變為100,
線程2從Block中恢復,獲取到的也是100,compare之后,繼續更新余額為50!!!
此時可以看到,實際余額應該為100(100-50+50),但是實際上變為了50(100-50+50-50)這就是ABA問題帶來的成功提交。

解決方法: 在變量前面加上版本號,每次變量更新的時候變量的版本號都+1,即A->B->A就變成了1A->2B->3A

循環時間長開銷大

如果CAS操作失敗,就需要循環進行CAS操作(循環同時將期望值更新為最新的),如果長時間都不成功的話,那么會造成CPU極大的開銷。

這種循環也稱為自旋

解決方法: 限制自旋次數,防止進入死循環。

只能保證一個共享變量的原子操作

CAS的原子操作只能針對一個共享變量。

解決方法: 如果需要對多個共享變量進行操作,可以使用加鎖方式(悲觀鎖)保證原子性,或者可以把多個共享變量合并成一個共享變量進行CAS操作。

CAS的應用

我們知道CAS操作并不會鎖住共享變量,也就是一種非阻塞的同步機制,CAS就是樂觀鎖的實現。

  1. 樂觀鎖 樂觀鎖總是假設最好的情況,每次去操作數據都認為不會被別的線程修改數據,所以在每次操作數據的時候都不會給數據加鎖,即在線程對數據進行操作的時候,別的線程不會阻塞仍然可以對數據進行操作,只有在需要更新數據的時候才會去判斷數據是否被別的線程修改過,如果數據被修改過則會拒絕操作并且返回錯誤信息給用戶。
  2. 悲觀鎖 悲觀鎖總是假設最壞的情況,每次去操作數據時候都認為會被的線程修改數據,所以在每次操作數據的時候都會給數據加鎖,讓別的線程無法操作這個數據,別的線程會一直阻塞直到獲取到這個數據的鎖。這樣的話就會影響效率,比如當有個線程發生一個很耗時的操作的時候,別的線程只是想獲取這個數據的值而已都要等待很久。

Java利用CAS的樂觀鎖、原子性的特性高效解決了多線程的安全性問題,例如JDK1.8中的集合類ConcurrentHashMap、關鍵字volatileReentrantLock等。

參考

JAVA CAS原理深度分析
Java CAS 原理分析
什么是ABA問題?

原文地址:ddnd.cn/2019/03/13/…

轉載于:https://juejin.im/post/5c87afa06fb9a049f1550b04

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

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

相關文章

在Windows Mobile模擬器(Emulator)建立網絡連接

因為想使用Windows Mobile Emulator進行網絡通信程序的測試,所以找方法配置Emulator的網絡連接。在網上找了一些文章,很多都說需要安裝Virtual PC 2007. 例如下面的文章Enable Network Connection Windows Mobile 6 Emulator 如果需要 Virtual PC 2007 可…

api游戲編程鼠標選擇拖動_如何選擇合適的游戲鼠標

api游戲編程鼠標選擇拖動You don’t need a gaming mouse to play PC games—just about any mouse with two buttons and a wheel will play anything you want it to. But that’s no reason to deny yourself the wonderful variety of gaming mouse designs on the market.…

iOS - 上架的APP 生成二維碼下載

1.首先打開蘋果App Store商店進入到里面,找到需要打開鏈接地址的應用程序,例如:百度。2. 在App Store商店里面先點擊一下應用程序圖標,再按一下…分享按鈕。 3. 接著選擇分享APP,再點擊拷貝鏈接地址,將應用…

Rsa2加密報錯java.security.spec.InvalidKeySpecException的解決辦法

最近在和支付寶支付做個對接,Java項目中用到了RSA2進行加解密,在加密過程中遇到了錯誤: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence 代碼執行到這句…

淺析領域驅動設計

1.概要DDD(Domain-driven design,模型驅動設計)是一種軟件設計的指導思想,而非固定的一套公式化開發模板(這樣就會導致網絡上出現各種基于自己或業務上的理解而產出的DDD落地的實現,會讓很想學習的開發者迷…

Delphi實現的透明陰影以及蒙版效果菜單

QQ2010的皮膚控件目前實現了一部分,看到有些軟件的菜單,都有陰影,透明等效果,于是開始重新實現菜單控件,QQ2009版的菜單控件,是自己從TComponent繼承了完全模擬實現的一個菜單,雖然實現了菜單控…

cortana搜索框_如何在Windows 10任務欄上隱藏Cortana搜索框

cortana搜索框One of the most talked about features in the latest version of Windows 10 was the Cortana personal assistant that is integrated directly into the taskbar. But what if you don’t want to waste all that taskbar space? 最新版本的Windows 10中最受…

Kotlin 基礎 - 數據類型

一、Boolean 類型 Boolean 值有兩個值,分別為 true 或 false。多數情況下,Kotlin 中的 Boolean 相當于 Java 中的基本類型 boolean,只有在必要的情況下才會裝箱成為 Java 中的裝箱類型 Boolean。這一切都是交由編譯器來完成,我們無…

全框眼鏡拆卸鏡片方法分享

全框眼鏡拆卸鏡片方法分享http://www.iqiyi.com/w_19ru97p1n9.html 很多直接用手掰就成(眼鏡布) 轉載于:https://www.cnblogs.com/OceanF/p/9288411.html

發送http請求

public static String httpGetSend(String url) {String responseMsg "";HttpClient httpClient new HttpClient();GetMethod getMethod new GetMethod(url);// GET請求try {// http超時5秒httpClient.getHttpConnectionManager().getParams().setConnectionTimeo…

微軟公布Entity Framework 8.0規劃

微軟.NET團隊在博客上公布了有關 Entity Framework Core 8.0(也稱為 EF Core 8 或 EF8)的未來規劃。EF Core 8 是 EF Core 7 之后的下一個版本,這將是一個長期支持版本;計劃于 2023 年 11 月與 .NET 8 同時發布。該公司表示&#…

roku能不能安裝軟件_如何阻止假期更改Roku主題

roku能不能安裝軟件Wondering why your Roku looks…different? Roku occasionally changes the background for its millions of users, something they call a “featured theme.” 想知道為什么您的Roku看起來...不同嗎? Roku偶爾會改變其數百萬用戶的背景&…

助力AIoT,雅觀科技發布空間智能化操作系統

雷鋒網(公眾號:雷鋒網)消息,3月14日,雅觀科技在上海舉辦了“「AI」悟及物 「柔」生萬屋”2019雅觀科技新品發布會,發布了空間智能化操作系統Akeeta、空間智能化柔性服務技術中臺Matrix,以及基于兩者開發的雅觀智慧社區…

HTTP與HTTPS區別(詳細)

轉:http://blog.sina.com.cn/s/blog_6eb3177a0102x66r.html 1、減少http請求(合并文件、合并圖片)2、優化圖片文件,減小其尺寸,特別是縮略圖,一定要按尺寸生成縮略圖然后調用,不要在網頁中用res…

Ajenti-Linux控制面板之自動化運維工具

ajenti http://ajenti.org/ https://github.com/ajenti/ajenti 源碼 http://docs.ajenti.org/en/latest/ http://docs.ajenti.org/en/latest/man/install.html# 安裝部署Fast remote access for every occasion Install once and never google for PuTTY downloads again. An…

MongoDB C# Driver 快速入門

MongoDB的官方C#驅動可以通過這個鏈接得到。鏈接提供了.msi和.zip兩種方式獲取驅動dll文件。C#驅動的基本數據庫連接,增刪改查操作。在使用C#驅動的時候,要在工程中添加"MongoDB.Bson.dll"和"MongoDB.Driver.dll"的引用。同時要在代…

如何在Windows 10的地圖應用程序中獲取離線地圖

If you know you’re going to be using your PC in a location without an Internet connection, and you need access to maps, you can download maps for specific areas in the “Maps” app in Windows 10 and use them offline. 如果您知道要在沒有Internet連接的地方使…

Hive初識(二)

Hive分區Hive組織表到分區。它是將一個表到基于分區列,如日期,城市和部門的值相關方式。使用分區,很容易對數據進行部分查詢。表或分區是細分成桶,以提供額外的結構,可以使用更高效的查詢的數據。桶的工作是基于表的一…

網站計數器 web映射

站點的網站計數器的操作 <% page import"java.math.BigInteger" %> <% page import"java.io.File" %> <% page import"java.util.Scanner" %> <% page import"java.io.FileInputStream" %> <% page import…

XenApp_XenDesktop_7.6實戰篇之八:申請及導入許可證

1. 申請許可證 Citrix XenApp_XenDesktop7.6和XenServer 6.5申請許可證的步驟是一致的&#xff0c;由于之前我已經申請過XenApp_XenDesktop的許可證&#xff0c;本次以XenServer6.5的許可證申請為例。 1.1 在申請試用或購買Citrix產品時&#xff0c;收到相應的郵件&#xff0…