国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

詳解java 中的CAS與ABA

發(fā)布時(shí)間:2021-07-06 11:13 來(lái)源:腳本之家 閱讀:0 作者:開(kāi)發(fā)架構二三事 欄目: 開(kāi)發(fā)技術(shù)

目錄

      1. 獨占鎖:

      屬于悲觀(guān)鎖,有共享資源,需要加鎖時(shí),會(huì )以獨占鎖的方式導致其它需要獲取鎖才能執行的線(xiàn)程掛起,等待持有鎖的錢(qián)程釋放鎖。傳統的關(guān)系型數據里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫(xiě)鎖等,都是在做操作之前先上鎖。Java中synchronized和ReentrantLock等獨占鎖就是悲觀(guān)鎖的思想。

      1.1 樂(lè )觀(guān)鎖的操作

      多線(xiàn)程并發(fā)修改一個(gè)值時(shí)的實(shí)現:

      public class SimulatedCAS {
      	//加volatile的目的是利用其happens-before原則,保證線(xiàn)程可見(jiàn)性
           private volatile int value;
       
           public synchronized int getValue() { return value; }
       
          public synchronized int compareAndSwap(int expectedValue, int newValue) {
               int oldValue = value;
               if (value == expectedValue)
                   value = newValue;
               return oldValue;
           }
      }

      2. 樂(lè )觀(guān)鎖:

      總是假設最好的情況,每次去拿數據的時(shí)候都認為別人不會(huì )修改,所以不會(huì )上鎖,但是在更新的時(shí)候會(huì )判斷一下在此期間別人有沒(méi)有去更新這個(gè)數據,可以使用版本號機制和CAS算法實(shí)現。樂(lè )觀(guān)鎖適用于多讀的應用類(lèi)型,這樣可以提高吞吐量,像數據庫提供的類(lèi)似于write_condition機制,其實(shí)都是提供的樂(lè )觀(guān)鎖。 在Java中java.util.concurrent.atomic包下面的原子變量類(lèi)就是使用了樂(lè )觀(guān)鎖的一種實(shí)現方式CAS實(shí)現的。樂(lè )觀(guān)鎖一般會(huì )使用版本號機制或CAS算法實(shí)現。

      2.1 CAS操作

      1. CAS 操作包含三個(gè)操作數 —— 內存位置(V)、預期原值(A)和新值(B)。如果內存位置的值與預期原值相匹配,那么處理器會(huì )自動(dòng)將該位置值更新為新值。否則,處理器不做任何操作。無(wú)論哪種情況,它都會(huì )在 CAS 指令之前返回該位置的值。(在 CAS 的一些特殊情況下將僅返回 CAS 是否成功,而不提取當前值。)CAS 有效地說(shuō)明了“我認為位置 V 應該包含值 A;如果包含該值,則將 B 放到這個(gè)位置;否則,不要更改該位置,只告訴我這個(gè)位置現在的值即可?!?/li>
      2. 通常將 CAS 用于同步的方式是從地址 V 讀取值 A,執行多步計算來(lái)獲得新值 B,然后使用 CAS 將 V 的值從 A 改為 B。如果 V 處的值尚未同時(shí)更改,則 CAS 操作成功。
      3. 類(lèi)似于 CAS 的指令允許算法執行讀-修改-寫(xiě)操作,而無(wú)需害怕其他線(xiàn)程同時(shí)修改變量,因為如果其他線(xiàn)程修改變量,那么 CAS 會(huì )檢測它(并失?。?,算法可以對該操作重新計算。 CAS實(shí)現計數器的操作:
      public class CasCounter {
          private SimulatedCAS value;
          public int getValue() {
              return value.getValue();
          }
          public int increment() {
              int oldValue = value.getValue();
              while (value.compareAndSwap(oldValue, oldValue + 1) != oldValue)
                  oldValue = value.getValue();
              return oldValue + 1;
          }
      }

      3. 原子變量類(lèi)

      JDK5.0之后加入了java.util.concurrent.atomic 包,其中的AtomicInteger; AtomicLong; AtomicReference; AtomicBoolean 等都是在CAS基礎上實(shí)現的。

      4. CAS的缺陷

      1. 循環(huán)時(shí)間太長(cháng),如果自旋長(cháng)時(shí)間不成功,會(huì )給cpu帶來(lái)極大的開(kāi)銷(xiāo),有興趣的可以使用JMH測試下AtomicLong 和 LongAdder的性能。
      2. ABA問(wèn)題: CAS需要檢查待操作值有沒(méi)有發(fā)生改變,如果沒(méi)有發(fā)生改變則更新。 但是存在這樣一種情況:如果一個(gè)值原來(lái)是A,變成了B,然后又變成了A,那么在CAS檢查的時(shí)候會(huì )發(fā)現沒(méi)有改變,但是實(shí)質(zhì)上它已經(jīng)發(fā)生了改變,這就是所謂的ABA問(wèn)題。 在運用CAS做Lock-Free操作中有一個(gè)經(jīng)典的ABA問(wèn)題:比如線(xiàn)程1從內存位置V中取出A,這時(shí)另一個(gè)線(xiàn)程2也從內存中取出A,并且線(xiàn)程2進(jìn)行了操作之后變成了B,然線(xiàn)程2又將V位置數據變成了A,這時(shí)候線(xiàn)程1進(jìn)行CAS操作發(fā)現內存中仍然是A,然后線(xiàn)程1 操作成功??瓷先ナ浅晒α?,實(shí)際上有隱藏的問(wèn)題: 現有一個(gè)用單向鏈表實(shí)現的FIFO堆棧,棧頂為A,這時(shí)線(xiàn)程1已經(jīng)知道A.next為B,然后希望用CAS將棧頂替換為B,在線(xiàn)程1執行上面這條指令之前,線(xiàn)程2 介入,將A、B出棧,再push D、C、A,此時(shí)A位于棧頂,B已經(jīng)不在棧中;此時(shí)線(xiàn)程1執行CAS,發(fā)現棧頂仍為A,所以CAS成功,即將棧頂變成B,但實(shí)際上此時(shí)B與 當前棧中元素D、C沒(méi)有關(guān)系,B.next為null,這樣一來(lái)就直接把C、D丟掉了。 對于A(yíng)BA問(wèn)題其解決方案是加上版本號,即在每個(gè)變量都加上一個(gè)版本號,每次改變時(shí)加1,即A —> B —> A,變成A(1) —> B(2) —> A(3)。 java中AtomicStampedReference也實(shí)現了這個(gè)作用,它通過(guò)包裝[E,Integer]的元組來(lái)對對象標記版本戳stamp,從而避免ABA問(wèn)題。
      public class AtomicTest {
      
      	private static AtomicInteger atomicInteger = new AtomicInteger(100);
      
      	private static AtomicStampedReference<Integer> atomicStampedReference =
      			new AtomicStampedReference<Integer>(99, 0);
      
      	public static void main(String[] args) throws InterruptedException {
      		Thread thread1 = new Thread(() -> {
      			atomicInteger.compareAndSet(99, 100);
      			atomicInteger.compareAndSet(100, 99);
      		});
      
      		Thread thread2 = new Thread(() -> {
      			try {
      				TimeUnit.SECONDS.sleep(1);
      			}catch (InterruptedException e){
      				e.printStackTrace();
      			}
      			boolean b = atomicInteger.compareAndSet(99, 100);
      			System.out.println(b);
      
      		});
      		thread1.start();
      		thread2.start();
      		thread1.join();
      		thread2.join();
      
      		Thread refT1 = new Thread(() -> {
      			try {
      				TimeUnit.SECONDS.sleep(1);
      			} catch (InterruptedException e) {
      				e.printStackTrace();
      			}
      			atomicStampedReference.compareAndSet(99, 100,
      					atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
      			atomicStampedReference.compareAndSet(100, 99,
      					atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
      		});
      
      		Thread refT2 = new Thread(() -> {
      			int stamp = atomicStampedReference.getStamp();
      			System.out.println("before sleep : stamp = " + stamp);    // stamp = 0
      			try {
      				TimeUnit.SECONDS.sleep(2);
      			} catch (InterruptedException e) {
      				e.printStackTrace();
      			}
      			System.out.println("after sleep : stamp = " + atomicStampedReference.getStamp());//stamp = 1
      			boolean c3 = atomicStampedReference.compareAndSet(99, 100, stamp, stamp+1);
      			System.out.println(c3);        //false
      		});
      		refT1.start();
      		refT2.start();
      	}
      }

      結果如下:

      true
      before sleep : stamp = 0
      after sleep : stamp = 2
      false

      也就是說(shuō)AtomicInteger更新成功,而AtomicStampedReference更新失敗。

      以上就是詳解java 中的CAS與ABA的詳細內容,更多關(guān)于java 中的CAS與ABA的資料請關(guān)注腳本之家其它相關(guān)文章!

      免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

      蜜桃视频在线观看免费视频网站WWW| AA级亚洲电影| 国产精品十八禁在线观看| 蜜桃视频一区二区三区在线观看| 1717国产精品久久| 99精品国产福久久久久久|