亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Compare And Swap底層原理及代碼示例詳解

 更新時(shí)間:2020年10月31日 15:23:14   作者:柒  
這篇文章主要介紹了Compare And Swap底層原理及代碼示例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

概念

CAS的全稱(chēng)是Compare-And-Swap,它是cpu并發(fā)原語(yǔ)

它的功能是判斷內(nèi)存某個(gè)位置的值是否為預(yù)期值。如果是則更改為新的值,這個(gè)過(guò)程是原子的

CAS并發(fā)原語(yǔ)體現(xiàn)在java語(yǔ)言中就是sun.misc.Unsafe類(lèi)的各個(gè)方法。調(diào)用UnSafe類(lèi)中的CAS方法,JVM會(huì)幫我們實(shí)現(xiàn)出CAS匯編指令,這是一種完全依賴(lài)于硬件的功能,通過(guò)它實(shí)現(xiàn)了原子操作,再次強(qiáng)調(diào),由于CAS是一種系統(tǒng)原語(yǔ),原語(yǔ)屬于操作系統(tǒng)用于范疇,是由若干條指令組成,用于完成某個(gè)功能的一個(gè)過(guò)程,并且原語(yǔ)的執(zhí)行必須是連續(xù)的,在執(zhí)行過(guò)程中不允許被中斷,也就是說(shuō)CAS是一條CPU的原子指令,不會(huì)造成所謂的數(shù)據(jù)不一致的問(wèn)題,也就是說(shuō)CAS是線(xiàn)程安全的。

代碼使用

首先使用AtomicInteger創(chuàng)建了一個(gè)實(shí)例,并初始化為5

// 創(chuàng)建一個(gè)原子類(lèi)

AtomicInteger atomicInteger= new AtomicInteger(5)

然后調(diào)用CAS方法,企圖更新成2019,這里有兩個(gè)參數(shù),一個(gè)是5,表示期望值,第二個(gè)就是我們要更新的值

atomicInteger.compareAndSet(5,2019)

然后再次使用了一個(gè)方法,同樣將值改為1024

atomicInteger.compareAndSet(5,1024)

完整代碼如下:

public class CASDemo {
  public static void main(String[] args) {
    // 創(chuàng)建一個(gè)原子類(lèi)
    AtomicInteger atomicInteger = new AtomicInteger(5);

    /**
     * 一個(gè)是期望值,一個(gè)是更新值,但期望值和原來(lái)的值相同時(shí),才能夠更改
     * 假設(shè)三秒前,我拿的是5,也就是expect為5,然后我需要更新成 2019
     */
    System.out.println(atomicInteger.compareAndSet(5, 2019) + "\t current data: " + atomicInteger.get());

    System.out.println(atomicInteger.compareAndSet(5, 1024) + "\t current data: " + atomicInteger.get());
  }
}

上面代碼的執(zhí)行結(jié)果為:


這是因?yàn)槲覀儓?zhí)行第一個(gè)的時(shí)候,期望值和原本值是滿(mǎn)足的,因此修改成功,但是第二次后,主內(nèi)存的值已經(jīng)改成了2019,不滿(mǎn)足期望值,因此返回了false,本次寫(xiě)入失敗


這個(gè)就類(lèi)似于SVN或者Git的版本號(hào),如果沒(méi)有人更改過(guò),就能夠正常提交,否者需要先將代碼pull下來(lái),合并代碼后,然后提交

CAS底層原理

首先我們先看看atomicInteger.getAndIncrement()方法的源碼


從這里能夠看到,底層又調(diào)用了一個(gè)unsafe類(lèi)的getAndAddInt方法

unsafe類(lèi)


Unsafe是CAS的核心類(lèi),由于Java方法無(wú)法直接訪問(wèn)底層系統(tǒng),需要通過(guò)本地(Native)方法來(lái)訪問(wèn),Unsafe相當(dāng)于一個(gè)后門(mén),基于該類(lèi)可以直接操作特定的內(nèi)存數(shù)據(jù),Unsafe類(lèi)存在sun.misc包中,其內(nèi)部方法操作可以像C指針一樣直接操作內(nèi)存,因?yàn)镴ava中的CAS操作的執(zhí)行依賴(lài)于Unsafe類(lèi)的方法。

注意Unsafe類(lèi)的所有方法都是native修飾的,也就是說(shuō)unsafe類(lèi)中的方法都直接調(diào)用操作系統(tǒng)底層資源執(zhí)行相應(yīng)的任務(wù)

為什么Atomic修飾的包裝類(lèi),能夠保證原子性,依靠的就是底層的unsafe類(lèi)

變量valueOffset

表示該變量值在內(nèi)存中的偏移地址,因?yàn)閁nsafe就是根據(jù)內(nèi)存偏移地址獲取數(shù)據(jù)的


從這里我們可以看到,通過(guò)valueOffset,直接通過(guò)內(nèi)存地址,獲取到值,然后進(jìn)行加1操作

變量value用volatile修飾

保證了多線(xiàn)程之間的內(nèi)存可見(jiàn)性


var5:就是我們從主內(nèi)存中拷貝到工作內(nèi)存中的值

那么操作的時(shí)候,需要比較工作內(nèi)存中的值,和主內(nèi)存中的值進(jìn)行比較

假設(shè)執(zhí)行 compareAndSwapInt返回false,那么就一直執(zhí)行 while方法,直到期望的值和真實(shí)值一樣

  • val1:AtomicInteger對(duì)象本身
  • var2:該對(duì)象值得引用地址
  • var4:需要變動(dòng)的數(shù)量
  • var5:用var1和var2找到的內(nèi)存中的真實(shí)值
    • 用該對(duì)象當(dāng)前的值與var5比較
    • 如果相同,更新var5 + var4 并返回true
    • 如果不同,繼續(xù)取值然后再比較,直到更新完成

這里沒(méi)有用synchronized,而用CAS,這樣提高了并發(fā)性,也能夠?qū)崿F(xiàn)一致性,是因?yàn)槊總€(gè)線(xiàn)程進(jìn)來(lái)后,進(jìn)入的do while循環(huán),然后不斷的獲取內(nèi)存中的值,判斷是否為最新,然后在進(jìn)行更新操作。

假設(shè)線(xiàn)程A和線(xiàn)程B同時(shí)執(zhí)行g(shù)etAndInt操作(分別跑在不同的CPU上)

  • tomicInteger里面的value原始值為3,即主內(nèi)存中AtomicInteger的 value 為3,根據(jù)JMM模型,線(xiàn)程A和線(xiàn)程B各自持有一份價(jià)值為3的副本,分別存儲(chǔ)在各自的工作內(nèi)存
  • 線(xiàn)程A通過(guò)getIntVolatile(var1 , var2) 拿到value值3,這是線(xiàn)程A被掛起(該線(xiàn)程失去CPU執(zhí)行權(quán))
  • 線(xiàn)程B也通過(guò)getIntVolatile(var1, var2)方法獲取到value值也是3,此時(shí)剛好線(xiàn)程B沒(méi)有被掛起,并執(zhí)行了compareAndSwapInt方法,比較內(nèi)存的值也是3,成功修改內(nèi)存值為4,線(xiàn)程B打完收工,一切OK
  • 這是線(xiàn)程A恢復(fù),執(zhí)行CAS方法,比較發(fā)現(xiàn)自己手里的數(shù)字3和主內(nèi)存中的數(shù)字4不一致,說(shuō)明該值已經(jīng)被其它線(xiàn)程搶先一步修改過(guò)了,那么A線(xiàn)程本次修改失敗,只能夠重新讀取后在來(lái)一遍了,也就是在執(zhí)行do while
  • 線(xiàn)程A重新獲取value值,因?yàn)樽兞縱alue被volatile修飾,所以其它線(xiàn)程對(duì)它的修改,線(xiàn)程A總能夠看到,線(xiàn)程A繼續(xù)執(zhí)行compareAndSwapInt進(jìn)行比較替換,直到成功。

Unsafe類(lèi) + CAS思想: 也就是自旋,自我旋轉(zhuǎn)底層匯編

Unsafe類(lèi)中的compareAndSwapInt是一個(gè)本地方法,該方法的實(shí)現(xiàn)位于unsafe.cpp中

  • 先想辦法拿到變量value在內(nèi)存中的地址
  • 通過(guò)Atomic::cmpxchg實(shí)現(xiàn)比較替換,其中參數(shù)X是即將更新的值,參數(shù)e是原內(nèi)存的值

CAS不加鎖,保證一次性,但是需要多次比較

  • 循環(huán)時(shí)間長(zhǎng),開(kāi)銷(xiāo)大(因?yàn)閳?zhí)行的是do while,如果比較不成功一直在循環(huán),最差的情況,就是某個(gè)線(xiàn)程一直取到的值和預(yù)期值都不一樣,這樣就會(huì)無(wú)限循環(huán))
  • 只能保證一個(gè)共享變量的原子操作
    • 當(dāng)對(duì)一個(gè)共享變量執(zhí)行操作時(shí),我們可以通過(guò)循環(huán)CAS的方式來(lái)保證原子操作
    • 但是對(duì)于多個(gè)共享變量操作時(shí),循環(huán)CAS就無(wú)法保證操作的原子性,這個(gè)時(shí)候只能用鎖來(lái)保證原子性
  • 引出來(lái)ABA問(wèn)題?

總結(jié)CAS

CAS是compareAndSwap,比較當(dāng)前工作內(nèi)存中的值和主物理內(nèi)存中的值,如果相同則執(zhí)行規(guī)定操作,否者繼續(xù)比較直到主內(nèi)存和工作內(nèi)存的值一致為止

CAS應(yīng)用

CAS有3個(gè)操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的更新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B,否者什么都不做

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java線(xiàn)程同步、同步方法實(shí)例詳解

    Java線(xiàn)程同步、同步方法實(shí)例詳解

    本篇文章主要通過(guò)實(shí)例介紹了Java線(xiàn)程:線(xiàn)程的同步-同步方法,需要的朋友可以參考下
    2017-04-04
  • springboot如何配置上傳文件的maxRequestSize

    springboot如何配置上傳文件的maxRequestSize

    這篇文章主要介紹了springboot如何配置上傳文件的maxRequestSize,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 基于Springboot+Junit+Mockito做單元測(cè)試的示例

    基于Springboot+Junit+Mockito做單元測(cè)試的示例

    本篇文章主要介紹了基于Springboot+Junit+Mockito做單元測(cè)試的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-02-02
  • Java多線(xiàn)程中的ReentrantLock可中斷鎖詳細(xì)解讀

    Java多線(xiàn)程中的ReentrantLock可中斷鎖詳細(xì)解讀

    這篇文章主要介紹了Java多線(xiàn)程中的ReentrantLock可中斷鎖詳細(xì)解讀,ReentrantLock中的lockInterruptibly()方法使得線(xiàn)程可以在被阻塞時(shí)響應(yīng)中斷,比如一個(gè)線(xiàn)程t1通過(guò)lockInterruptibly()方法獲取到一個(gè)可重入鎖,并執(zhí)行一個(gè)長(zhǎng)時(shí)間的任務(wù),需要的朋友可以參考下
    2023-12-12
  • 深入理解final變量的初始化

    深入理解final變量的初始化

    本篇文章是對(duì)final變量的初始化進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • 如何發(fā)布jar包到maven中央倉(cāng)庫(kù)

    如何發(fā)布jar包到maven中央倉(cāng)庫(kù)

    這篇文章主要介紹了發(fā)布jar包到maven中央倉(cāng)庫(kù)的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-12-12
  • Spring中@Autowired注解的原理詳解

    Spring中@Autowired注解的原理詳解

    這篇文章主要介紹了Spring中@Autowired注解的原理詳解,對(duì)于spring配置一個(gè)bean時(shí),如果需要給該bean提供一些初始化參數(shù),則需要通過(guò)依賴(lài)注入方式,所謂的依賴(lài)注入就是通過(guò)spring將bean所需要的一些參數(shù)傳遞到bean實(shí)例對(duì)象的過(guò)程,需要的朋友可以參考下
    2023-11-11
  • Spring CGLlB動(dòng)態(tài)代理實(shí)現(xiàn)過(guò)程解析

    Spring CGLlB動(dòng)態(tài)代理實(shí)現(xiàn)過(guò)程解析

    這篇文章主要介紹了Spring CGLlB動(dòng)態(tài)代理實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • 使用Spring Data Redis實(shí)現(xiàn)數(shù)據(jù)緩存的方法

    使用Spring Data Redis實(shí)現(xiàn)數(shù)據(jù)緩存的方法

    目前在系統(tǒng)架構(gòu)設(shè)計(jì)中使用Redis實(shí)現(xiàn)緩存,這篇文章主要介紹了使用Spring Data Redis實(shí)現(xiàn)數(shù)據(jù)緩存的方法,具有一定的參考價(jià)值,需要的朋友可以參考下
    2018-11-11
  • Docker 解決openjdk容器里無(wú)法使用JDK的jmap等命令問(wèn)題

    Docker 解決openjdk容器里無(wú)法使用JDK的jmap等命令問(wèn)題

    這篇文章主要介紹了Docker 解決openjdk容器里無(wú)法使用JDK的jmap等命令問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12

最新評(píng)論