java併發編程學習13--Atomic數據結構簡介

【介紹

JAVA 中無鎖的線程安全整數 AtomicInteger,一個提供原子操做的Integer的類。在Java語言中,++i和i++操做並非線程安全的,在使用的時候,不可避免的會用到synchronized關鍵字。而AtomicInteger則經過一種線程安全的加減操做接口。AtomicInteger爲何可以達到多而不亂,處理高併發應付自如呢?這是由硬件提供原子操做指令實現的,這裏面用到了一種併發技術:CAS。在非激烈競爭的狀況下,開銷更小,速度更快。
Java.util.concurrent中實現的原子操做類包括:java

AtomicBoolean、AtomicInteger、AtomicIntegerArray、AtomicLong、AtomicReference、AtomicReferenceArray。

【核心:基於CAS的樂觀鎖實現

1.悲觀鎖與樂觀鎖:程序員

- 悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認爲別人會修改,因此每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關係型數據庫裏邊就用到了不少這種鎖機制,好比行鎖,表鎖等,讀鎖,寫鎖等,都是在作操做以前先上鎖。
 - 樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認爲別人不會修改,因此不會上鎖,可是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣能夠提升吞吐量,像數據庫若是提供相似於write_condition機制的其實都是提供的樂觀鎖。

兩種鎖各有優缺點,不可認爲一種好於另外一種,像樂觀鎖適用於寫比較少的狀況下,即衝突真的不多發生的時候,這樣能夠省去了鎖的開銷,加大了系統的整個吞吐量。但若是常常產生衝突,上層應用會不斷的進行retry,這樣反卻是下降了性能,因此這種狀況下用悲觀鎖就比較合適。數據庫

2.CAS:安全

CAS就是Compare and Swap的意思,比較並操做。不少的cpu直接支持CAS指令。CAS是項樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知此次競爭中失敗,並能夠再次嘗試。CAS有3個操做數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作。多線程

private static final Unsafe unsafe = Unsafe.getUnsafe();

【與synchronized與ReentrantLock的比較

  • synchronized:
    在資源競爭不是很激烈的狀況下,偶爾會有同步的情形下,synchronized是很合適的。緣由在於,編譯程序一般會盡量的進行優化synchronize,另外可讀性很是好,無論用沒用過5.0多線程包的程序員都能理解。
  • ReentrantLock:
    ReentrantLock提供了多樣化的同步,好比有時間限制的同步,能夠被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在資源競爭不激烈的情形下,性能稍微比synchronized差點點。可是當同步很是激烈的時候,synchronized的性能一會兒能降低好幾十倍。而ReentrantLock確還能維持常態(1.7後synchronized已經優化,性能差距很小)。
  • Atomic:
    和上面的相似,不激烈狀況下,性能比synchronized略遜,而激烈的時候,也能維持常態。激烈的時候,Atomic的性能會優於ReentrantLock一倍左右。可是其有一個缺點,就是隻能同步一個值,一段代碼中只能出現一個Atomic的變量,多於一個同步無效。由於他不能在多個Atomic之間同步。

【例子

package AtomicData;

import java.util.concurrent.atomic.AtomicInteger;

public class Test {

    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        MyThread my = new MyThread(test);
        for (int i = 0; i < 500; i++) {
            Thread t = new Thread(my);
            t.start();
        }
        //等待子線程執行完
        Thread.sleep(5000);
        System.out.println(test.number);
    }

    AtomicInteger number = new AtomicInteger(0);
    protected void update() {
        number.getAndAdd(1);
    }
}
class MyThread implements Runnable {

    Test t;

    public MyThread(Test t){
        this.t =t;
    }

    @Override
    public void run() {
        t.update();
    }
}
相關文章
相關標籤/搜索