日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁 > > 架構(gòu)師社區(qū)
[導(dǎo)讀]簡介 CAS的全稱是compare and swap,它是java同步類的基礎(chǔ),java.util.concurrent中的同步類基本上都是使用CAS來實現(xiàn)其原子性的。 CAS的原理其實很簡單,為了保證在多線程環(huán)境下我們的更新是符合預(yù)期的,或者說一個線程在更新某個對象的時候,沒有其他的線程

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


簡介

CAS的全稱是compare and swap,它是java同步類的基礎(chǔ),java.util.concurrent中的同步類基本上都是使用CAS來實現(xiàn)其原子性的。


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


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


那么CAS在使用中會有什么問題呢?


一般來說CAS如果設(shè)計的不夠完美的話,可能會產(chǎn)生ABA問題,而ABA問題又可以分為兩類,我們先看來看一類問題。


第一類問題

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


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

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

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

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

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


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


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


2,3兩步可能出現(xiàn)的情況就在像C++這種,不存在自動GC環(huán)境的編程語言中。


因為可以自己控制對象的生命周期,如果我們從一個list中刪除掉了一個對象,然后又重新分配了一個對象,并將其add back到list中去,那么根據(jù) MRU memory allocation算法,這個新的對象很有可能和之前刪除對象的內(nèi)存地址是一樣的。這樣就會導(dǎo)致ABA的問題。


第二類問題

如果我們在擁有自動GC的編程語言中,那么是否仍然存在CAS問題呢?


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


考慮下面的步驟:


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

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

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

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

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


我們舉個例子:


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


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


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

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

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


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


第二類問題可能會導(dǎo)致某些集合類的操作并不是原子性的,因為你并不能保證在CAS的過程中,有沒有其他的節(jié)點發(fā)送變化。


第一類問題的解決

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


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


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

  2. 使用自動GC。

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

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

第二類問題的解決

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


我們舉個AtomicStampedReference的例子:


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


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


總結(jié)

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

特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:

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

長按訂閱更多精彩▼

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

如有收獲,點個在看,誠摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉