1. 程式人生 > >AtomicInteger如何保證執行緒安全以及樂觀鎖/悲觀鎖的概念

AtomicInteger如何保證執行緒安全以及樂觀鎖/悲觀鎖的概念

最近面試被問到一個問題,AtomicInteger如何保證執行緒安全?我查閱了資料 發現還可以引申到 樂觀鎖/悲觀鎖的概念,覺得值得一記。

眾所周知,JDK提供了AtomicInteger保證對數字的操作是執行緒安全的,執行緒安全我首先想到了synchronized和Lock,但是這種方式又有一個名字,叫做互斥鎖,一次只能有一個持有鎖的執行緒進入,再加上還有不同執行緒爭奪鎖這個機制,效率比較低,所以又稱“悲觀鎖”。

但是相應的有了樂觀鎖的概念,他的思路就是,它不加鎖去完成某項操作,如果因為衝突失敗就重試,直到成功為止。這種說的比較抽象,我們直接拿AtomicInteger原始碼舉例,因為AtomicInteger保證執行緒安全就是因為使用了樂觀鎖。

Unsafe 是做一些Java語言不允許但是又十分有用的事情,具體的實現都是native方法,AtomicInteger裡呼叫的 Unsafe 方法 基於的是CPU 的 CAS指令來實現的。所以基於 CAS 的操作可認為是無阻塞的,一個執行緒的失敗或掛起不會引起其它執行緒也失敗或掛起。並且由於 CAS 操作是 CPU 原語,所以效能比較好。

    /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final
int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); }

CAS就是Compare and Swap的意思,比較並操作。很多的cpu直接支援CAS指令。CAS是項樂觀鎖技術,當多個執行緒嘗試使用CAS同時更新同一個變數時,只有其中一個執行緒能更新變數的值,而其它執行緒都失敗,失敗的執行緒並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。CAS有3個運算元,記憶體值V,舊的預期值A,要修改的新值B。當且僅當預期值A和記憶體值V相同時,將記憶體值V修改為B,否則什麼都不做。

從程式碼上我們可以看到do while語句,從而證實當更新出現衝突時,即失敗時,它還會嘗試更新。符合樂觀鎖的思想。

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;
    }

最後比較一下 樂觀鎖/悲觀鎖的 區別

樂觀鎖適用於寫比較少的情況下,即衝突比較少發生,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。
但如果經常產生衝突,樂觀鎖 的重複嘗試 反倒會降低了效能,所以這種情況下用悲觀鎖就比較合適。