CAS原理

CAS指令和具體源代碼

源碼說話,咱們看一下getAndIncrement方法:java

 1 //該方法功能是Interger類型加1
 2 public final int getAndIncrement() {
 3     //主要看這個getAndAddInt方法
 4     return unsafe.getAndAddInt(this, valueOffset, 1);
 5 }
 6 
 7 //var1 是this指針
 8 //var2 是地址偏移量
 9 //var4 是自增的數值,是自增1仍是自增N
10 public final int getAndAddInt(Object var1, long var2, int var4) {
11     int var5;
12     do {
13       //獲取內存值,這是內存值已是舊的,假設咱們稱做指望值E
14        var5 = this.getIntVolatile(var1, var2);
15        //compareAndSwapInt方法是重點,
16        //var5是指望值,var5 + var4是要更新的值
17        //這個操做就是調用CAS的JNI,每一個線程將本身內存裏的內存值M
18        //與var5指望值E做比較,若是相同將內存值M更新爲var5 + var4,不然作自旋操做
19     } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
20 
21     return var5;
22 }
解釋一下getAndAddInt方法的流程:

假設有一下情景:多線程

  1. A、B兩個線程
  2. jvm主內存的值1,A、B工做內存的值爲1(工做內存會拷貝一份主內存的值)
  3. 當前指望值爲1,作加1操做
  4. 此時var5 = 1,var4 = 1;
    1. A線程將var5與工做內存值M比較,比較var5是否等於1
    2. 若是相同則將工做內存值修改成var5 + var4 即修改成2並同步到駐村,此時this + valueOffset指針裏,示例變量value的值就是2,結束循環
    3. 若是不相同,則是B線程修改了主內存的值,說明B線程已經先於A線程作了加1操做,A線程沒有更新成功須要繼續循環,注意此時var5更新爲新的內存值,假設當前的內存值是2,那麼此時var5 = 2,var + var4 = 3,重複上述步驟直到成功(自旋),成功以後,內存地址中的值就改變爲3

簡單來講,就是將工做內存的值與主內存的值來進行比較,若是相等,說明沒有被其餘線程修改,則進行賦新值操做,若是不相等,說明主內存的數據被其餘線程更改過,則此時須要更新工做內存的副本值,而且從新獲取主內存的值,再次進行比較,直到兩者相等爲止;jvm

CAS優缺點

  • 優勢

非阻塞的輕量級的樂觀鎖,經過CPU指令實現,在資源競爭不激烈的狀況下性能高,相比synchronized重量鎖,synchronized會進行比較複雜的加鎖、解鎖和喚醒操做。性能

  • 缺點
  1. ABA問題: 線程C、D;線程D將A修改成B後又修改成A,此時C線程覺得A沒有改變過,java的原子類AtomicStampedReference,經過控制變量值的版本號來保證CAS的正確性。具體解決思路就是在變量前追加上版本號,每次變量更新的時候把版本號加一,那麼A - B - A就會變成1A - 2B - 3A。
  1. 自旋時間過長,消耗CPU資源,若是資源競爭激烈,多線程自旋長時間消耗資源
相關文章
相關標籤/搜索