最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

分析ABA問題的本質(zhì)及其解決辦法

 更新時(shí)間:2021年06月02日 10:58:22   作者:flydean  
CAS的全稱是compare and swap,它是java同步類的基礎(chǔ),java.util.concurrent中的同步類基本上都是使用CAS來實(shí)現(xiàn)其原子性的。本文將介紹ABA問題的本質(zhì)及其解決辦法。

簡(jiǎn)介

CAS的原理其實(shí)很簡(jiǎn)單,為了保證在多線程環(huán)境下我們的更新是符合預(yù)期的,或者說一個(gè)線程在更新某個(gè)對(duì)象的時(shí)候,沒有其他的線程對(duì)該對(duì)象進(jìn)行修改。在線程更新某個(gè)對(duì)象(或值)之前,先保存更新前的值,然后在實(shí)際更新的時(shí)候傳入之前保存的值,進(jìn)行比較,如果一致的話就進(jìn)行更新,否則失敗。

注意,CAS在java中是用native方法來實(shí)現(xiàn)的,利用了系統(tǒng)本身提供的原子性操作。

那么CAS在使用中會(huì)有什么問題呢?一般來說CAS如果設(shè)計(jì)的不夠完美的話,可能會(huì)產(chǎn)生ABA問題,而ABA問題又可以分為兩類,我們先看來看一類問題。

第一類問題

我們考慮下面一種ABA的情況:

1.在多線程的環(huán)境中,線程a從共享的地址X中讀取到了對(duì)象A。

2.在線程a準(zhǔn)備對(duì)地址X進(jìn)行更新之前,線程b將地址X中的值修改為了B。

3.接著線程b將地址X中的值又修改回了A。

4.最新線程a對(duì)地址X執(zhí)行CAS,發(fā)現(xiàn)X中存儲(chǔ)的還是對(duì)象A,對(duì)象匹配,CAS成功。

上面的例子中CAS成功了,但是實(shí)際上這個(gè)CAS并不是原子操作,如果我們想要依賴CAS來實(shí)現(xiàn)原子操作的話可能就會(huì)出現(xiàn)隱藏的bug。

第一類問題的關(guān)鍵就在2和3兩步。這兩步我們可以看到線程b直接替換了內(nèi)存地址X中的內(nèi)容。

在擁有自動(dòng)GC環(huán)境的編程語言,比如說java中,2,3的情況是不可能出現(xiàn)的,因?yàn)樵趈ava中,只要兩個(gè)對(duì)象的地址一致,就表示這兩個(gè)對(duì)象是相等的。

2,3兩步可能出現(xiàn)的情況就在像C++這種,不存在自動(dòng)GC環(huán)境的編程語言中。因?yàn)榭梢宰约嚎刂茖?duì)象的生命周期,如果我們從一個(gè)list中刪除掉了一個(gè)對(duì)象,然后又重新分配了一個(gè)對(duì)象,并將其add back到list中去,那么根據(jù) MRU memory allocation算法,這個(gè)新的對(duì)象很有可能和之前刪除對(duì)象的內(nèi)存地址是一樣的。這樣就會(huì)導(dǎo)致ABA的問題。

第二類問題

如果我們?cè)趽碛凶詣?dòng)GC的編程語言中,那么是否仍然存在CAS問題呢?

考慮下面的情況,有一個(gè)鏈表里面的數(shù)據(jù)是A->B->C,我們希望執(zhí)行一個(gè)CAS操作,將A替換成D,生成鏈表D->B->C??紤]下面的步驟:

1.線程a讀取鏈表頭部節(jié)點(diǎn)A。

2.線程b將鏈表中的B節(jié)點(diǎn)刪掉,鏈表變成了A->C

3.線程a執(zhí)行CAS操作,將A替換從D。

最后我們的到的鏈表是D->C,而不是D->B->C。

問題出在哪呢?CAS比較的節(jié)點(diǎn)A和最新的頭部節(jié)點(diǎn)是不是同一個(gè)節(jié)點(diǎn),它并沒有關(guān)心節(jié)點(diǎn)A在步驟1和3之間是否內(nèi)容發(fā)生變化。

我們舉個(gè)例子:

public void useABAReference(){
    CustUser a= new CustUser();
    CustUser b= new CustUser();
    CustUser c= new CustUser();
    AtomicReference<CustUser> atomicReference= new AtomicReference<>(a);
    log.info("{}",atomicReference.compareAndSet(a,b));
    log.info("{}",atomicReference.compareAndSet(b,a));
    a.setName("change for new name");
    log.info("{}",atomicReference.compareAndSet(a,c));
}

上面的例子中,我們使用了AtomicReference的CAS方法來判斷對(duì)象是否發(fā)生變化。在CAS b和a之后,我們將a的name進(jìn)行了修改,我們看下最后的輸出結(jié)果:

[main] INFO com.flydean.aba.ABAUsage - true

[main] INFO com.flydean.aba.ABAUsage - true

[main] INFO com.flydean.aba.ABAUsage - true

三個(gè)CAS的結(jié)果都是true。說明CAS確實(shí)比較的兩者是否為統(tǒng)一對(duì)象,對(duì)其中內(nèi)容的變化并不關(guān)心。

第二類問題可能會(huì)導(dǎo)致某些集合類的操作并不是原子性的,因?yàn)槟悴⒉荒鼙WC在CAS的過程中,有沒有其他的節(jié)點(diǎn)發(fā)送變化。

第一類問題的解決

第一類問題在存在自動(dòng)GC的編程語言中是不存在的,我們主要看下怎么在C++之類的語言中解決這個(gè)問題。

根據(jù)官方的說法,第一類問題大概有四種解法:

1.使用中間節(jié)點(diǎn) - 使用一些不代表任何數(shù)據(jù)的中間節(jié)點(diǎn)來表示某些節(jié)點(diǎn)是標(biāo)記被刪除的。

2.使用自動(dòng)GC。

3.使用hazard pointers - hazard pointers 保存了當(dāng)前線程正在訪問的節(jié)點(diǎn)的地址,在這些hazard pointers中的節(jié)點(diǎn)不能夠被修改和刪除。

4.使用read-copy update (RCU) - 在每次更新的之前,都做一份拷貝,每次更新的是拷貝出來的新結(jié)構(gòu)。

第二類問題的解決

第二類問題其實(shí)算是整體集合對(duì)象的CAS問題了。一個(gè)簡(jiǎn)單的解決辦法就是每次做CAS更新的時(shí)候再添加一個(gè)版本號(hào)。如果版本號(hào)不是預(yù)期的版本,就說明有其他的線程更新了集合中的某些節(jié)點(diǎn),這次CAS是失敗的。

我們舉個(gè)AtomicStampedReference的例子:

public void useABAStampReference(){
    Object a= new Object();
    Object b= new Object();
    Object c= new Object();
    AtomicStampedReference<Object> atomicStampedReference= new AtomicStampedReference(a,0);
    log.info("{}",atomicStampedReference.compareAndSet(a,b,0,1));
    log.info("{}",atomicStampedReference.compareAndSet(b,a,1,2));
    log.info("{}",atomicStampedReference.compareAndSet(a,c,0,1));
}

AtomicStampedReference的compareAndSet方法,多出了兩個(gè)參數(shù),分別是expectedStamp和newStamp,兩個(gè)參數(shù)都是int型的,需要我們手動(dòng)傳入。

總結(jié)

ABA問題其實(shí)是由兩類問題組成的,需要我們分開來對(duì)待和解決。

以上就是分析ABA問題的本質(zhì)及其解決辦法的詳細(xì)內(nèi)容,更多關(guān)于ABA問題的本質(zhì)及其解決辦法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 關(guān)于Scanner對(duì)象的輸入結(jié)束標(biāo)記問題

    關(guān)于Scanner對(duì)象的輸入結(jié)束標(biāo)記問題

    這篇文章主要介紹了關(guān)于Scanner對(duì)象的輸入結(jié)束標(biāo)記問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • MyBatis-Plus數(shù)據(jù)庫(kù)配置與數(shù)據(jù)源整合方案

    MyBatis-Plus數(shù)據(jù)庫(kù)配置與數(shù)據(jù)源整合方案

    本文詳細(xì)介紹了在MyBatis-Plus中進(jìn)行數(shù)據(jù)庫(kù)配置與數(shù)據(jù)源整合的常見方法,包括單數(shù)據(jù)源和多數(shù)據(jù)源的配置步驟,以及如何使用SpringBoot的自動(dòng)配置和手動(dòng)配置來管理數(shù)據(jù)源,通過合理的配置,開發(fā)者可以簡(jiǎn)化數(shù)據(jù)庫(kù)操作,實(shí)現(xiàn)高效的數(shù)據(jù)庫(kù)管理和復(fù)雜的應(yīng)用架構(gòu)
    2025-02-02
  • 解決mybatis-plus新增數(shù)據(jù)自增ID變無序問題

    解決mybatis-plus新增數(shù)據(jù)自增ID變無序問題

    這篇文章主要介紹了解決mybatis-plus新增數(shù)據(jù)自增ID變無序問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。
    2023-07-07
  • 基于Springboot2.0構(gòu)建ES的多客戶端

    基于Springboot2.0構(gòu)建ES的多客戶端

    這篇文章主要為大家詳細(xì)介紹了基于Springboot2.0構(gòu)建ES的多客戶端,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • JavaSE系列基礎(chǔ)包裝類及日歷類詳解

    JavaSE系列基礎(chǔ)包裝類及日歷類詳解

    這篇文章主要介紹的是JavaSE中常用的基礎(chǔ)包裝類以及日歷類的使用詳解,文中的示例代碼簡(jiǎn)潔易懂,對(duì)我們學(xué)習(xí)JavaSE有一定的幫助,感興趣的小伙伴快來跟隨小編一起學(xué)習(xí)吧
    2021-12-12
  • spring hibernate實(shí)現(xiàn)動(dòng)態(tài)替換表名(分表)的方法

    spring hibernate實(shí)現(xiàn)動(dòng)態(tài)替換表名(分表)的方法

    下面小編就為大家?guī)硪黄猻pring hibernate實(shí)現(xiàn)動(dòng)態(tài)替換表名(分表)的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • mybatis-plus實(shí)體類主鍵策略有3種(小結(jié))

    mybatis-plus實(shí)體類主鍵策略有3種(小結(jié))

    這篇文章主要介紹了mybatis-plus實(shí)體類主鍵策略有3種(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Java實(shí)現(xiàn)二分查找BinarySearch算法

    Java實(shí)現(xiàn)二分查找BinarySearch算法

    這篇文章主要介紹了Java實(shí)現(xiàn)二分查找BinarySearch算法,二分查找針對(duì)的是一個(gè)有序的數(shù)據(jù)集合,每次都通過跟區(qū)間的中間元素對(duì)比,將待查找的區(qū)間縮小為之前的一半,直到找到要查找的元素,或者區(qū)間被縮小為 0,需要的朋友可以參考下
    2023-12-12
  • springboot 配置文件配置項(xiàng)前綴為0的數(shù)字特殊處理方式

    springboot 配置文件配置項(xiàng)前綴為0的數(shù)字特殊處理方式

    這篇文章主要介紹了springboot 配置文件配置項(xiàng)前綴為0的數(shù)字特殊處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java中的異常處理(try,catch,finally,throw,throws)

    Java中的異常處理(try,catch,finally,throw,throws)

    本文主要介紹了Java中的異常處理,文章主要介紹的異常處理包括5個(gè)關(guān)鍵字try,catch,finally,throw,throws,更多詳細(xì)內(nèi)容需要的朋友可以參考一下
    2022-06-06

最新評(píng)論

信阳市| 太保市| 珲春市| 江城| 牟定县| 广水市| 滨海县| 灌阳县| 浦东新区| 辛集市| 定陶县| 南投县| 大悟县| 兰州市| 绥滨县| 南宁市| 即墨市| 广州市| 柘荣县| 成都市| 桂林市| 九江市| 额济纳旗| 佛教| 铜山县| 都安| 达州市| 贺兰县| 阿图什市| 东源县| 中阳县| 阿克苏市| 临武县| 高青县| 积石山| 同心县| 涟水县| 万州区| 寿光市| 泸定县| 巴彦淖尔市|