<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          CAS算法與Java原子類

          共 1602字,需瀏覽 4分鐘

           ·

          2020-10-26 18:47

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          ? 作者?|??低吟不作語(yǔ)

          來(lái)源 |? urlify.cn/eYzAZr

          66套java從入門(mén)到精通實(shí)戰(zhàn)課程分享

          1、樂(lè)觀鎖

          一般而言,在并發(fā)情況下我們必須通過(guò)一定的手段來(lái)保證數(shù)據(jù)的準(zhǔn)確性,如果沒(méi)有做好并發(fā)控制,就可能導(dǎo)致臟讀、幻讀和不可重復(fù)度等一系列問(wèn)題。樂(lè)觀鎖是人們?yōu)榱藨?yīng)付并發(fā)問(wèn)題而提出的一種思想,具體的實(shí)現(xiàn)則有多種方式。

          樂(lè)觀鎖假設(shè)數(shù)據(jù)一般情況下不會(huì)造成沖突,只在數(shù)據(jù)進(jìn)行提交更新時(shí),才會(huì)正式對(duì)數(shù)據(jù)的沖突與否進(jìn)行檢測(cè),如果發(fā)現(xiàn)沖突了,則返回給用戶錯(cuò)誤的信息,讓用戶決定如何去做。樂(lè)觀鎖適用于讀操作多的場(chǎng)景,可以提高程序的吞吐量。

          2、CAS

          CAS(Compare And Swap)比較并交換,是一種實(shí)現(xiàn)了樂(lè)觀鎖思想的并發(fā)控制技術(shù)。CAS 算法的過(guò)程是:它包含 3 個(gè)參數(shù) CAS(V,E,N),V 表示要更新的變量(內(nèi)存值),E 表示舊的預(yù)期值,N 表示即將更新的預(yù)期值。當(dāng)且僅當(dāng) V 值等于 E 值時(shí),才會(huì)將 V 的值設(shè)為 N,如果 V 值和 E 值不同,說(shuō)明已經(jīng)有其他線程做了更新,則當(dāng)前線程什么也不做,并返回當(dāng)前 V 的真實(shí)值。整個(gè)操作是原子性的。

          當(dāng)多個(gè)線程同時(shí)使用 CAS 操作一個(gè)變量時(shí),只有一個(gè)會(huì)勝出,并成功更新,其余均會(huì)失敗。失敗的線程不會(huì)被掛起,僅是被告知失敗,并允許再次嘗試,當(dāng)然也可以放棄本次操作,所以 CAS 算法是非阻塞的。基于上述原理,CAS 操作可以在不借助鎖的情況下實(shí)現(xiàn)合適的并發(fā)處理。


          3、ABA 問(wèn)題

          ABA 問(wèn)題是 CAS 算法的一個(gè)漏洞。CAS 算法實(shí)現(xiàn)的一個(gè)重要前提是:取出內(nèi)存中某時(shí)刻的數(shù)據(jù),并在下一時(shí)刻比較并替換,在這個(gè)時(shí)間差內(nèi)可能會(huì)導(dǎo)致數(shù)據(jù)的變化。

          假設(shè)有兩個(gè)線程,分別要對(duì)內(nèi)存中某一變量做 CAS 操作,線程一先從內(nèi)存中取出值 A,線程二也從內(nèi)存中取出值 A,并把值從 A 變?yōu)?B 寫(xiě)回,然后又把值從 B 變?yōu)?A 寫(xiě)回,這時(shí)候線程一進(jìn)行 CAS 操作,發(fā)現(xiàn)內(nèi)存中的值還是 A,于是認(rèn)為和預(yù)期值一致,操作成功。盡管線程一的 CAS 操作成功,但并不代表這個(gè)過(guò)程就沒(méi)有問(wèn)題。

          ABA 問(wèn)題會(huì)帶來(lái)什么隱患呢?維基百科給出了詳細(xì)的示例:假設(shè)現(xiàn)有一個(gè)用單鏈表實(shí)現(xiàn)的堆棧,棧頂為 A,A.next = B,現(xiàn)有線程一希望用 CAS 把棧頂替換為 B,但在此之前,線程二介入,將 A、B 出棧,再壓入 D、C、A,整個(gè)過(guò)程如下

          此時(shí) B 處于游離轉(zhuǎn)態(tài),輪到線程一執(zhí)行 CAS 操作,發(fā)現(xiàn)棧頂仍為 A,CAS 成功,棧頂變?yōu)?B,但實(shí)際上 B.next = null,即堆棧中只有 B 一個(gè)元素,C 和 D 并不在堆棧中,平白無(wú)故就丟了。簡(jiǎn)單來(lái)說(shuō),ABA 問(wèn)題使我們漏掉某一段時(shí)間的數(shù)據(jù)監(jiān)控,誰(shuí)知道在這段時(shí)間內(nèi)會(huì)發(fā)生什么有趣(可怕)的事呢?

          可以通過(guò)版本號(hào)的方式來(lái)解決 ABA 問(wèn)題,每次執(zhí)行數(shù)據(jù)修改操作時(shí),都會(huì)帶上一個(gè)版本號(hào),如果版本號(hào)和數(shù)據(jù)的版本一致,對(duì)數(shù)據(jù)進(jìn)行修改操作并對(duì)版本號(hào) +1,否則執(zhí)行失敗。因?yàn)槊看尾僮鞯陌姹咎?hào)都會(huì)隨之增加,所以不用擔(dān)心出現(xiàn) ABA 問(wèn)題。

          4、使用 Java 模擬 CAS 算法

          這僅僅是基于 Java 層面上的模擬,真正的實(shí)現(xiàn)要涉及到底層(我學(xué)不會(huì))

          public?class?TestCompareAndSwap?{

          ????private?static?CompareAndSwap?cas?=?new?CompareAndSwap();

          ????public?static?void?main(String[]?args)?{

          ????????for?(int?i?=?0;?i?????????????new?Thread(new?Runnable()?{
          ????????????????public?void?run()?{
          ????????????????????//?獲取預(yù)估值
          ????????????????????int?expectedValue?=?cas.get();
          ????????????????????boolean?b?=?cas.compareAndSet(expectedValue,?(int)?(Math.random()?*?101));
          ????????????????????System.out.println(b);
          ????????????????}
          ????????????});
          ????????}
          ????}
          }

          class?CompareAndSwap?{

          ????private?int?value;

          ????//?獲取內(nèi)存值
          ????public?synchronized?int?get()?{
          ????????return?value;
          ????}

          ????//?比較
          ????public?synchronized?int?compareAndSwap(int?expectedValue,?int?newValue)?{
          ????????//?讀取內(nèi)存值
          ????????int?oldValue?=?value;
          ????????//?比較
          ????????if?(oldValue?==?expectedValue)?{
          ????????????this.value?=?newValue;
          ????????}
          ????????return?oldValue;
          ????}

          ????//?設(shè)置
          ????public?synchronized?boolean?compareAndSet(int?expectedValue,?int?newValue)?{
          ????????return?expectedValue?==?compareAndSwap(expectedValue,?newValue);
          ????}
          }


          5、原子類


          原子包 java.util.concurrent.atomic 提供了一組原子類,原子類的操作具有原子性,一旦開(kāi)始,就一直運(yùn)行直到結(jié)束,中間不會(huì)有任何線程上下文切換。原子類的底層正是基于 CAS 算法實(shí)現(xiàn)線程安全。

          Java 為我們提供了十六個(gè)原子類,可以大致分為以下四種:

          1. 基本類型

          • AtomicBoolean

            原子更新布爾類型,內(nèi)部使用 int 類型的 value 存儲(chǔ) 1 和 0 表示 true 和 false,底層也是對(duì) int 類型的原子操作

          • AtomicInteger

            原子更新 int 類型

          • AtomicLong

            原子更新 long 類型

          2. 引用類型

          • AtomicReference

            原子更新引用類型,通過(guò)泛型指定要操作的類

          • AtomicMarkableReference

            原子更新引用類型,內(nèi)部維護(hù)一個(gè) Pair 類型(靜態(tài)內(nèi)部類)的成員屬性,其中有一個(gè) boolean 類型的標(biāo)志位,避免 ABA 問(wèn)題

          • private?static?class?Pair?{
            ????final?T?reference;
            ????final?boolean?mark;
            ????private?Pair(T?reference,?boolean?mark)?{
            ????????this.reference?=?reference;
            ????????this.mark?=?mark;
            ????}
            ????static??Pair?of(T?reference,?boolean?mark)?{
            ????????return?new?Pair(reference,?mark);
            ????}
            }

            private?volatile?Pair?pair
          • AtomicStampedReference

            原子更新引用類型,內(nèi)部維護(hù)一個(gè) Pair 類型(靜態(tài)內(nèi)部類)的成員屬性,其中有一個(gè) int 類型的郵戳(版本號(hào)),避免 ABA 問(wèn)題

          • private?static?class?Pair?{
            ????final?T?reference;
            ????final?int?stamp;
            ????private?Pair(T?reference,?int?stamp)?{
            ????????this.reference?=?reference;
            ????????this.stamp?=?stamp;
            ????}
            ????static??Pair?of(T?reference,?int?stamp)?{
            ????????return?new?Pair(reference,?stamp);
            ????}
            }

            private?volatile?Pair?pair


          3. 數(shù)組類型

          • AtomicIntegerArray

            原子更新 int 數(shù)組中的元素

          • AtomicLongArray

            原子更新 long 數(shù)組中的元素

          • AtomicReferenceArray

            原子更新 Object 數(shù)組中的元素


          4. 對(duì)象屬性類型

          用于解決對(duì)象的屬性的原子操作

          • AtomicIntegerFieldUpdater

            原子更新對(duì)象中的 int 類型字段

          • AtomicLongFieldUpdater

            原子更新對(duì)象中的 long 類型字段

          • AtomicReferenceFieldUpdater

            原子更新對(duì)象中的引用類型字段

          之前提到的三種類型的使用都比較簡(jiǎn)單,查閱對(duì)應(yīng) API 即可,而對(duì)象屬性類型則有一些限制:

          • 字段必須是 volatile 類型的,在線程之間共享變量時(shí)保證立即可見(jiàn)

          • 只能是實(shí)例變量,不能是類變量,也就是說(shuō)不能加 static 關(guān)鍵字

          • 只能是可修改變量,不能使用 final 變量

          • 該對(duì)象字段能夠被直接操作,因?yàn)樗腔诜瓷鋵?shí)現(xiàn)的


          5. 高性能原子類

          Java8 新增的原子類,使用分段的思想,把不同的線程 hash 到不同的段上去更新,最后再把這些段的值相加得到最終的值。以下四個(gè)類都繼承自 Striped64,對(duì)并發(fā)的優(yōu)化在 Striped64 中實(shí)現(xiàn)

          • LongAccumulator

            long 類型的聚合器,需要傳入一個(gè) long 類型的二元操作,可以用來(lái)計(jì)算各種聚合操作,包括加乘等

          • LongAdder

            long 類型的累加器,LongAccumulator 的特例,只能用來(lái)計(jì)算加法,且從 0 開(kāi)始計(jì)算

          • DoubleAccumulator

            double 類型的聚合器,需要傳入一個(gè) double 類型的二元操作,可以用來(lái)計(jì)算各種聚合操作,包括加乘等

          • DoubleAdder

            double 類型的累加器,DoubleAccumulator 的特例,只能用來(lái)計(jì)算加法,且從 0 開(kāi)始計(jì)算




          粉絲福利:實(shí)戰(zhàn)springboot+CAS單點(diǎn)登錄系統(tǒng)視頻教程免費(fèi)領(lǐng)取

          ???

          ?長(zhǎng)按上方微信二維碼?2 秒
          即可獲取資料



          感謝點(diǎn)贊支持下哈?

          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产做受91 一片二片老头 | 骚逼看片| 欧美精品第一区 | 亚洲夜色在线 | 免费视频在线观看a |