Java原子性操作之——Atomic包的原理分析



Atomic:

    Atomic包是java.util.concurrent下的另一個專門為線程安全設計的java的包,包含多個原子性操作的類。基本特性就是在多線程情況下,當多個線程想要同時操作這些類的某些實例方法時,具有排他性,也就是當某個線程在執行某個方法時,不會被其他線程打斷,其他線程會在外部等待,一直等到該方法執行完畢,才由JVM從等待隊列中選擇另一個線程進入,這只是一種邏輯上的理解。實際上是借助硬件的相關指令來實現的,不會阻塞線程(只是在硬件級別去阻塞了)。可以對基本數據,數組中的基本數據,對類中的基本數據進行操作。原子變量類相當於一種泛化的volatile變量,能夠支持原子的和有條件的讀寫操作。

    我們先看一下傳統的鎖是怎樣保證線程安全的

   

class LockDemo {

    private int a;

    public synchronized void setA(int b) {
        this.a = b;
    }

    public synchronized int getA() {
        return a;
    }

    public synchronized void addA() {
        ++a;
    }

    public synchronized void reduceA() {
        --a;
    }

}
其實這樣的synchronized已經能滿足我們日常的線程安全需求了,synchronized是基於代碼阻塞的機制,也就是當某個線程占用資源時,其他線程是無法進入的,如果這個線程出現問題的時候,出現大量線程阻塞,CPU就會耗費大量資源來處理阻塞在外的這些線程,但是CPU的任務本不該如此,還極可能出現死鎖等問題,對於這樣的簡單操作反而顯得有些笨重,所以應該有更合適更高效的方法來處理這樣的問題。所以就有了CAS

Compare and swap(CAS)

當前的處理器基本都支持CAS,這是一種基於硬件的處理,每一個CAS操作都包含三個運算符:內存地址V , 一個期望值A和新值B,操作的時候如果這個地址上存放的值等於期望的值A,那么就賦值為B,如果沒有,那么就不做任何操作。簡而言之,你的值和我的期望值相等,就給你新值,否則不干活了。

我們自己可以簡單的模擬一下CAS的實現

class CASDemo {

    private int value;

    public synchronized int getValue() {
        return value;
    }

    public synchronized int cas(int expectValue , int newValue) {

        int oldValue = value;
        if(value == expectValue) {
            value = newValue;
        }
        return oldValue;

    }

}

class CASTest {

    private CASDemo casDemo;

    public int getValue () {
        return casDemo.getValue();
    }

    public int add () {
        int oldValue = casDemo.getValue();
        while(casDemo.cas(oldValue , oldValue + 1) != oldValue) {
            oldValue = casDemo.getValue();
        }

        return oldValue + 1;
    }

}
看下Atomic包



看一下AtomicInteger類是怎么處理自增的處理的,也就是方法getAndIncrement()



這里調用了一個叫做Unsafe類的方法,這個類比較特殊內部大多是native的方法,而且這個類也不允許我們隨意使用,當然JDK自己是可以用的,(ps:處於凡是試試的態度,我試了一下調用它的方法,報錯如下)

看一下valueOffset是哪來的

static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}
這里又調用了Unsafe類的一個方法,參數是通過反射獲取的AtomicInteger的value屬性,也就是它的值

繼續進

public native long objectFieldOffset(Field var1);


很可惜,是一個本地方法,查看文檔可知,這個方法返回的就是"原來的值"的內存地址 , valueOffset的值(ps:文檔是其他某位大神那里借來的)

返回一開始的方法 點進去看

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}
參數比較混亂 var2就是valueOffset 繼續跟進

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
喔,又是一個本地方法,參數很亂  按我們之前的講就是:

 var2(valueOffset)  , var4(expectValue) , var5(newValue)

我再借一點注釋



這個方法原子的將變量的值更新為var5,如果成功了返回true。這樣返回去取反即為false,循環結束,返回var5,取到新值。

當然Unsafe關於CAS的方法都是本地方法,是由C語言實現的,我們這里是看不到具體實現細節的。



    說了半天 到底CAS是怎么保證線程安全的呢,其實在語言層次我們是沒有做任何關於同步的操作的,也沒有任何鎖。Atomic包下這些類將這些操作都交給了CPU和內存,利用CPU多處理的能力,實現硬件的阻塞,再加上volatile變量的特性即可實現基於原子性的操作的線程安全。所以CAS不是沒有阻塞 ,只是阻塞不是語言層面,而是在硬件層面,這樣便會更高效。


免責聲明!

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



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