[Java] 簡單分析AtomicInteger中的addAndget方法


簡單使用AtomicInteger

首先貼出以下的代碼,簡單的使用AtomicInteger這個類來實現+1的操作。

import java.util.concurrent.atomic.AtomicInteger;

public class CASTest {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(10);

        for (int i = 0; i < 10; i ++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    atomicInteger.addAndGet(1);
                }
            }).start();
        }

        System.out.println(atomicInteger);
    }
}

打印的值是20,可見是線程安全的。以下逐步分析其中蘊含的CAS原理。

提供增加操作的addAndGet方法

分析其中的addAndGet方法,其源碼如下

public final int addAndGet(int delta) {
    return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}

發現它是調用unsafe包的方法,首先對傳給unsafe的getAnddAddInt的參數進行說明:

  • this指當前的AtomicInteger對象
  • valueOffset由AtmoicInteger類中的靜態代碼塊確定,指的是AtomicInteger的value屬性在內存中的偏移量
  • delta即要value值要改變的大小

實際執行修改操作的getAndAddInt方法

再來看unsafe包中的getAndAddInt的方法,為cas原理的核心方法

public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);
    } while (!compareAndSwapInt(o, offset, v, v + delta));
    return v;
}

這個方法返回的值其實是修改前的值,所以addAndGet方法返回的是改方法的返回值 + delta
主要有以下步驟:

  1. do循環中通過getIntVolatile本地方法來獲取主存中的Atomic的value值,並賦值給局部變量v
  2. compareAndSwapInt來同樣通過o,offset參數來獲取主存中的value值,代碼中並未體現
  3. 通過比較v與compareAndSwapInt方法中再次獲得的值來判斷修改是否執行,即v = v + delta操作
  4. 成功則跳出循環,返回修改前的值
  5. 不成功則一直循環,自旋在此處

其他方法

其中getIntVolatile與compareAndSwapInt方法簽名如下

//** Volatile version of {@link #getInt(Object, long)}  
public native int getIntVolatile(Object o, long offset);

//Atomically update Java variable to x if it is currently holding expected.Returns:true if successful
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);

CAS的缺點

可以看到CAS原理其中是不斷比較最初的值,和要進行修改時的值進行比較,來判斷是否有其他線程修改了此變量;如果變量被修改,則不會繼續修改變量。但是CAS是一種自旋的操作,會占用CPU的時間塊分配的。以及存在這樣一個邏輯漏洞:如果一個變量V初次讀取的時候是A值,並且在准備賦值的時候檢查到它仍然是A值,那我們就能說明它的值沒有被其他線程修改過了嗎?如果在這段期間它的值曾經被改成了B,然后又改回A,那CAS操作就會誤認為它從來沒有被修改過。這個漏洞稱為CAS操作的"ABA"問題。


免責聲明!

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



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