1. 程式人生 > >【一】關於java.util.concurrent包下的併發類(atomic)

【一】關於java.util.concurrent包下的併發類(atomic)

併發類包除了java.util.concurrent之外,還有java.util.concurrent.atomic和java.util.concurrent.lock.java.util.concurrent中主要是一些關於集合框架的併發實現,例如ConcurrentHashMap。多執行緒任務執行類:Callable(被執行的任務)、Executor(執行任務)和Future(非同步提交任務的返回資料)等等。以及執行緒管理類:例如CyclicBarrier、CountDownLatch和Exchanger。java.util.concurrent.atomic實現了原子化操作的資料型別:例如AtomicBoolean、AtomicInteger等等。java.util.concurrent.lock實現了併發操作中的幾種型別的鎖。 這一塊LZ並不熟悉,故而會分若干篇文章進行歸納總結。應該總結的內容也比較流於表面。大體的意思能弄明白能描述清楚就已經很好了。1、atomic包下的相關類這個包下的類大體上通過了解volatile關鍵字和CAS演算法大體就可以了。以AtomicInteger舉例,它存在一個被volatile修飾的變數value。這個value在初始化時會被設定為initialValue或者預設為0。而此時這個value就是後文所要提到的原來的值。同時存在一個final型別的靜態變數valueOffset,這個變數會在隨後的靜態程式碼塊中被賦值(通過sun.misc包下的Unsafe類獲取到原來的值的偏移地址)。被volatile修飾的變數,其儲存於共享記憶體中,而非在各個執行緒的專屬儲存空間。即這個變數是被所有執行緒所共享的、可見的。每次讀取的都是記憶體中該變數的最新值。在這個過程中涉及到記憶體屏障。所謂記憶體屏障(一個CPU指令)的作用就是告訴CPU和編譯器在這個屏障之前的命令必須先執行完成,在屏障之後的命令必須後執行。另一個作用是強制更新一次不同CPU的快取。故而,被volatile修飾的變數,其在寫操作後會插入一個寫屏障,在讀操作前插入一個讀屏障。這便意味著你完成了寫操作之後,任意執行緒得到的都是最新的值,而且在你讀取之前會保證所以之前的事均已發生,並且任何新的值都會被重新整理到記憶體中。但是這裡要注意,volatile是不能保證原子的可見性的,除了Long型別和Double型別除外。因為在32位的作業系統之中,CPU一次只能讀取32位,而這兩個型別均是64位,其讀寫操作會分為兩步進行。CAS演算法:什麼是CAS演算法(compareAndSwap)?其基本思想是原本的值與期望的值進行比較,如若相等,則更新當前值為新值。不相等則不做任何事,但要返回原值。下面引用了一個博主的簡單的CAS實現:public class SimulatedCAS {
    private int value;

    public synchronized int getValue() {
        return value;
    }

    public synchronized int compareAndSwap(int expectedValue, int newValue) {
        int oldValue = value;
        if (value == expectedValue) //如若value和期望值相等

            value = newValue;//將newValue複製給value
        System.out.println("value" + value);
           return oldValue;//返回原來的值}
}
public class CasCounter {
    private SimulatedCAS value = new SimulatedCAS();

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

    public int increment() {
        int oldValue = value.getValue();
        System.out.println(oldValue);
        while (value.compareAndSwap(oldValue, oldValue + 1) != oldValue)//value返回的值和獲取到的值相同

            oldValue = value.getValue();

        return oldValue + 1;//返回原來的值+1
    }
    public static void main(String[] args) {
        CasCounter casCounter = new CasCounter();
        System.out.println(casCounter.increment()); //輸出1
        System.out.println(casCounter.increment());//輸出2
    }
}
上述例子基本闡明瞭CAS的基本思想。在AtomicInteger中,getAndIncrement方法便利用了CAS的思想,通過Unsafe的compareAndSwap比較期望的值和valueOffset記憶體地址指向的值是否一致,如若一致,則更新為newValue。上述以AtomicInteger為例,簡要的說明了一下volatile關鍵字以及CAS演算法的基本思想。實際上在atomic包中,類似於AtomicBoolean、AtomicLong等類的實現方式與AtomicInteger基本一致。其均是用CAS演算法實現getAndSet、Increment、decrement等等操作。可以看到在整個atomic中不存在任何的鎖物件,它保持原子性的方式是通過CAS,將原本有語言層次的阻塞性交給了CPU、硬體層面。從而使得操作更有效率。(具體如何沒有詳加了解)