Java併發中正確使用volatile

前幾天併發編程羣裏有同窗對volatile的用法提出了疑問,恰好我記得Twitter有關實時搜索的這個PPT對這個問題解釋的很清晰並有一個實際的應用場景,因而週末把這個問題摘錄了一些和併發相關的內容以下:html

目錄:

併發 - 定義

悲觀鎖 - Pressimistic locking

  1. 一個線性在執行一個操做時持有對一個資源的獨佔鎖。(互斥)

  2. 通常用在衝突比較可能發生的場景下

樂觀鎖 - Optimistic locking

  1. 嘗試採用原子操做,而不須要持有鎖;衝突可被檢測,若是發生衝突,具備相應的重試邏輯

  2. 一般用在衝突較少發生的場景下

非阻塞算法 - Non-blocking algorithm

  1. 算法確保對線程間競爭共享資源時候,不會由於互斥而使任一線程的執行無限延遲;

無鎖算法 - Lock-free algorithm

  1. 若是系統整個流程的執行是無阻塞的(系統某一部分可能被短暫阻塞),這種非阻塞算法就是無鎖的。

  2. 無鎖算法比傳統的基於鎖的算法對系統的開銷更小,且更容易在多核多CPU處理器上擴展;

  3. 在實時系統中能夠避免鎖帶來的延遲;

  4. CAS (compare and swap)或LL/SC(load linked/store conditional),以及內存屏障相關的指令常常被用在算法實現中。

無等待算法 - Wait-free algorithm

  1. 若是每一個線程的執行都是無阻塞的,這種非阻塞算法就是無等待的(比無鎖算法更好)

Java的併發

  1. Java的內存模型並不保證一個線程能夠一直以程序執行的順序看到另外一個線程對變量的修改,除非兩個線程都跨越了同一個內存屏障。(Safe publication)

Java內存模型

代碼順序規則

  1. 一個線程內的每一個動做 happens-before 同一個線程內在代碼順序上在其後的全部動做

volatile變量規則

  1. 對一個volatile變量的讀,老是能看到(任意線程)對這個volatile變量最後的寫入

傳遞性

  1. 若是A happens-before B, B happens-before C,那 A happens-before C

Safe publication案例

class VolatileExample {
    int x = 0;
    volatile int b = 0;
    private void write() {
        x = 5;
        b = 1;
    }
    private void read() {
        int dummy = b;
        while (x!=5) {
        }
    }
    public static void main(String[] args) throws Exception {
        final VolatileExample example = new VolatileExample();
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                example.write();
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                example.read();
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
    }
}


x並不須要定義爲volatile, 程序裏能夠有須要相似x的變量,咱們只須要一個volatile變量b來確保線程a能看到線程1對x的修改:

  1. 根據happens-before的傳遞規則,線程1的x=5; happens-before b=1;; 線程2的int dummy = b; happens-beforewhile(x!=5);

  2. 根據volatile變量規則,線程2的b=1; happens-before int dummy=b;

  3. 根據傳遞性,x=5; happens-before while(x!=5);

JSR-133

在JSR-133以前的舊Java內存模型中,雖然不容許volatile變量之間重排序,但舊的Java內存模型仍然會容許volatile變量與普通變量之間重排序。JSR-133則加強了volatile的內存語義:嚴格限制編譯器(在編譯器)和處理器(在運行期)對volatile變量與普通變量的重排序,確保volatile的寫-讀和監視器的釋放-獲取同樣,具備相同的內存語義。

延伸閱讀: JSR-133: JavaTM Memory Model and Thread Specification, The JSR-133 Cookbook for Compiler Writers

參考連接

  1. http://2011.lucene-eurocon.org/attachments/0002/8787/Busch_twitter_realtime_search_eurocon_11.pdf

  2. http://www.rossbencina.com/code/lockfree

  3. http://rethinkdb.com/blog/lock-free-vs-wait-free-concurrency/

  4. http://www.infoq.com/cn/articles/java-memory-model-4

  5. JSR-133: JavaTM Memory Model and Thread Specification

  6. The JSR-133 Cookbook for Compiler Writers

相關文章
相關標籤/搜索