1. 程式人生 > >什麼是java中CAS

什麼是java中CAS

問題一:java中的CAS是什麼?

問題二:為什麼要使用CAS?

問題三:CAS使用中需要注意什麼問題?

 這裡以提問的方式引出話題,下面帶大家慢慢了解CAS。

1.CAS的含義

      CAS是compare and swap的縮寫,即我們所說的比較交換。cas是一種基於鎖的操作,而且是樂觀鎖。在java中鎖分為樂觀鎖和悲觀鎖。悲觀鎖是將資源鎖住,等一個之前獲得鎖的執行緒釋放鎖之後,下一個執行緒才可以訪問。而樂觀鎖採取了一種寬泛的態度,通過某種方式不加鎖來處理資源,比如通過給記錄加version來獲取資料,效能較悲觀鎖有很大的提高。

       CAS 操作包含三個運算元 —— 記憶體位置(V)、預期原值(A)和新值(B)。如果記憶體地址裡面的值和A的值是一樣的,那麼就將記憶體裡面的值更新成B。CAS是通過無限迴圈來獲取資料的,若果在第一輪迴圈中,a執行緒獲取地址裡面的值被b執行緒修改了,那麼a執行緒需要自旋,到下次迴圈才有可能機會執行。

2.CAS的問題

      ①.CAS容易造成ABA問題。一個執行緒a將數值改成了b,接著又改成了a,此時CAS認為是沒有變化,其實是已經變化過了,而這個問題的解決方案可以使用版本號標識,每操作一次version加1。在java5中,已經提供了AtomicStampedReference來解決問題。

     ②.CAS造成CPU利用率增加。之前說過了CAS裡面是一個迴圈判斷的過程,如果執行緒一直沒有獲取到狀態,cpu資源會一直被佔用。

3.看AutoInteger的實現原始碼

     其實AutoInteger就是使用了CAS來實現加1,我們知道如果有一個共享變數count=1,開5個執行緒,每個執行緒加20次,結果一般來說都會小於100.

@Test public void test20() throws InterruptedException {     for (int i = 1; i <= 5; i++) {         MyThrend thrend = new MyThrend("thead" + i);         Thread thread = new Thread(thrend);         thread.start();     }     Thread.sleep(2000);     System.out.println(MyCount1.count); }

static class MyThrend implements Runnable {     private String name;     MyThrend(String threadName) {         this.name = threadName;     }

    @Override     public void run() {         for (int i=0;i<20;i++)         MyCount1.count++;     } }

private static class MyCount1 {     static int count = 0; }

結果78 

現在修改一個程式碼,將int變成AtomicInteger

@Test public void test20() throws InterruptedException {     for (int i = 1; i <= 5; i++) {         MyThrend thrend = new MyThrend("thead" + i);         Thread thread = new Thread(thrend);         thread.start();     }     Thread.sleep(2000);     System.out.println(MyCount.count.get()); }

static class MyThrend implements Runnable {     private String name;     MyThrend(String threadName) {         this.name = threadName;     }

    @Override     public void run() {         for (int i=0;i<20;i++)         MyCount.count.getAndIncrement();  //加1方法     } }

private static class MyCount {     static AtomicInteger count = new AtomicInteger(0); } 每次結果都是100,怎麼做到的呢?這裡是沒有直接加鎖的,看原始碼。

public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);  //第一個引數當前物件地址,第二個引數資料偏移量,第三個引數每次指定預設加1
}

public final int getAndAddInt(Object var1, long var2, int var4) {  //這個方法使用的就是CAS,核心在於迴圈比較記憶體裡面的值和當前值是否相等,如果相等就用新值覆蓋     int var5;        do {         var5 = this.getIntVolatile(var1, var2);  //如果a,b執行緒同時執行這個方法,a執行緒拿到值1後cpu執行時間到了掛起,b開始執行,也拿到1,但是沒有掛起,接著將值變成了2     } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));  //這個時候a執行緒恢復執行,去比較的時候發現手上的1 和記憶體裡面的值2不等,這個時候他要進行下一個迴圈,看出來了佔用cpu吧     return var5; } AtomicInteger,AtomicLong,AtomicBoolean.....都在java.util.current.atomic包下面,採用了CAS機制來實現加鎖,裡面具體方法就不看了,留給大家後續研究!