java CAS原理

1、CAS簡介

1. CAS是什麼?

     CAS全稱是Compare and Swap,即比較並交換,是經過原子指令來實現多線程的同步功能,將獲取存儲在內存地址的原值和指定的內存地址進行比較,只有當他們相等時,交換指定的預期值和內存中的值,這個操做是原子操做,若不相等,則從新獲取存儲在內存地址的原值。java

2. CAS的流程

     CAS是一種無鎖算法,有3個關鍵操做數,內存地址,舊的內存中預期值,要更新的新值,當內存值和舊的內存中預期值相等時,將內存中的值更新爲新值。算法

3.樂觀鎖與悲觀鎖

     CAS屬於樂觀鎖,樂觀鎖就是每次不加鎖而是假設沒有衝突而去完成某項操做,若是由於衝突失敗就重試,直到成功爲止。
     synchronized是悲觀鎖,被一個線程拿到鎖以後,其餘線程必須等待該線程釋放鎖,性能較差安全

2、AtomicInteger代碼演示

     在java中,a++不是原子操做,一個簡單的a++操做涉及到三個操做,獲取變量a的內存值,將變量a+1,將新值寫入內存,這裏涉及到了兩次內存訪問,若是在多線程環境下,那麼會出現併發安全問題。
     AtomicInteger是一個原子操做類,內部採用的就是CAS無鎖算法。 這裏咱們分析一下它的內部實現。bash

AtomicInteger atomicInteger = new AtomicInteger(0);
atomicInteger.getAndSet(1);  
複製代碼

     這裏的靜態代碼塊AtomicInteger對象初始化以前就執行,獲取AtomicInteger對象value字段相對AtomicInteger對象的」起始地址」的偏移量,Java對象在內存中存儲的佈局能夠分爲三塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding),」起始地址」的偏移量便是對象頭的偏移量。多線程

static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}
複製代碼
public final int getAndSet(int newValue) {
    return unsafe.getAndSetInt(this, valueOffset, newValue);
}
複製代碼

     每次經過內存地址(var2)先從內存中獲取內存中原值(var5),再循環將內存中的原值(var5)與給定內存地址(var2)相比較,若是相等則更新指定預期值(var4),若是不相等則再重試直到成功爲止,最後返回舊的內存原值var5。併發

//var1爲AtomicInteger對象,var2爲內存地址值,var4爲指定的預期值
public final int getAndSetInt(Object var1, long var2, int var4) {
    int var5;
    do {
	//unsafe.getIntVolatile調用本地方法獲取內存中值
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var4));

    return var5;
}
複製代碼

3、弊端

1. ABA問題

     CAS在操做的時候會檢查變量的值是否被更改過,若是沒有則更新值,可是帶來一個問題,最開始的值是A,接着變成B,最後又變成了A。通過檢查這個值確實沒有修改過,由於最後的值仍是A,可是實際上這個值確實已經被修改過了。爲了解決這個問題,在每次進行操做的時候加上一個版本號,每次操做的就是兩個值,一個版本號和某個值,A——>B——>A問題就變成了1A——>2B——>3A。在jdk中提供了AtomicStampedReference類解決ABA問題,用Pair這個內部類實現,包含兩個屬性,分別表明版本號和引用,在compareAndSet中先對當前引用進行檢查,再對版本號標誌進行檢查,只有所有相等才更新值。佈局

2. 只能保證一個共享變量的原子操做

     多個共享變量操做時,循環CAS就沒法保證操做的原子性,這個時候就能夠用鎖。從java1.5開始,JDK提供了AtomicReference類來保證引用對象之間的原子性,就能夠把多個變量放在一個對象裏來進行CAS操做。post

3. 循環時間長CPU開銷較大

     在併發量比較高的狀況下,若是許多線程反覆嘗試更新某一個變量,卻又一直更新不成功,循環往復,會給CPU帶來很大的壓力。性能

做者:陶章好
連接:juejin.im/post/5d63ea… 來源:掘金
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。ui

相關文章
相關標籤/搜索