具體CAS操作實現(無鎖算法)


具體CAS操作

上一篇講述了CAS機制,這篇講解CAS具體操作.

什么是悲觀鎖、樂觀鎖?在java語言里,總有一些名詞看語義跟本不明白是啥玩意兒,也就總有部分面試官拿着這樣的詞來忽悠面試者,以此來找優越感,其實理解清楚了,這些詞也就唬不住人了。

  • synchronized是悲觀鎖,這種線程一旦得到鎖,其他需要鎖的線程就掛起的情況就是悲觀鎖。

  • CAS操作的就是樂觀鎖,每次不加鎖而是假設沒有沖突而去完成某項操作,如果因為沖突失敗就重試,直到成功為止。

那么問題來了,什么是CAS操作?

CAS是Compare-and-swap(比較與替換)的簡寫,是一種有名的無鎖算法,在java中,我們主要分析Unsafe類,因為所有的CAS操作都是它來實現的,而在Unsafe類中這些方法也都是native方法

    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

  public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

  public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

看到上面的解釋是不是索然無味,查找了很多資料也沒完全弄明白,通過幾次驗證后,終於明白,最終可以理解成一個無阻塞多線程爭搶資源的模型。先上代碼

package com.company.reentrantLock;

import java.util.concurrent.atomic.AtomicBoolean;

public class AtomicBooleanTest implements Runnable{
  public static AtomicBoolean exits = new AtomicBoolean(true);
  public static void main(String[] args) {
      AtomicBooleanTest abd = new AtomicBooleanTest();
      Thread t1 = new Thread(abd);
      Thread t2 = new Thread(abd);
      t1.start();
      t2.start();
  }

  @Override
  public void run() {
      System.out.println("begin run");
      System.out.println("real " + exits.get());
      if(exits.compareAndSet(true,false)){
          System.out.println(Thread.currentThread().getName() + " " + exits.get() );
          exits.set(true);
      }else{
          run();
      }
  }
}

輸出結果:

begin run
real true
Thread-1 false
begin run
real true
Thread-0 false

這里無論怎么運行,Thread-1、Thread-0都會執行if=true條件,而且還不會產生線程臟讀臟寫,這是如何做到的了,這就用到了我們的compareAndSet(boolean expect,boolean update)方法,先上圖簡單講解下程序原理,然后再分析compareAndSet作用。

 

 

這個圖中重最要的是compareAndSet(true,false)方法要拆開成compare(true)方法和Set(false)方法理解,是compare(true)是等於true后,就馬上設置共享內存為false,這個時候,其它線程無論怎么走都無法走到只有得到共享內存為true時的程序隔離方法區。 但是這種得不到狀態為true時使用遞歸算法是很耗cpu資源的,所以一般情況下,都會有線程sleep。

 

總結

         這篇文章並沒有展開講compareAndSet底層調用的是unsafe.compareAndSwapInt方法,因為這是native方法,很多人都會展開找源碼,最后也只找到是調用CPU方法,沒講到具體用法,如果只用compareAndSet(true,false)舉例則更加簡單。 這種無阻塞式的多線程操作數據,在大並發情況下,是一筆非常可觀的性能提升,所以,如果在大並發或多線程性能要求高的情況下有更加好的技術選型,可以參考這種底層實現。

       結合JMM知識,線程間共享的變量,首先在主存中會保留一份,然后每個線程的工作內存也會保留一份副本.

我們對比發現,這里的預期值就是線程保留的副本,當該線程從主存中獲取該變量值后,主存中該變量可能已經被其他線程刷新了,但是該線程工作內存中該變量卻還是原來的值,這就是所謂的預期值.

當你要CAS刷新該值的時候,如果發現線程工作內存和主存中不一致,就會失敗.如果一致,就可以更新成功

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM