1. 程式人生 > >聊聊高並發(十九)理解並發編程的幾種"性" -- 可見性,有序性,原子性

聊聊高並發(十九)理解並發編程的幾種"性" -- 可見性,有序性,原子性

sock clas 關註 條件 infoq zed 應該 單獨 ssa

這篇的主題本應該放在最初的幾篇。討論的是並發編程最基礎的幾個核心概念。可是這幾個概念又牽扯到非常多的實際技術。比方Java內存模型。各種鎖的實現,volatile的實現。原子變量等等,每個都可以展開寫非常多,尤其是Java內存模型,網上已經可以有非常幾篇不錯的文章,臨時不想反復造輪子。這裏推薦幾篇Jave內存模型的資料:

1. JSR-133 FAQ

2. JSR-133 Cookbook

3. Synchronization and Java Memory Model

4. 深入理解Java內存模型


我之前也寫了一個Java內存模型的PPT: http://share.csdn.net/slides/7916


以下說說並發編程關註的幾個核心概念。

關註一個並發問題,有3個主要的關註點:

1. 安全性。也就是正確性。指的是程序在並發情況下運行的結果和預期一致

2. 活躍性,比方死鎖。活鎖

3. 性能,降低上下文切換。降低內核調用。降低一致性流量等等


安全性問題是首要解決的問題。保證程序的線程安全。實際上就是對多線程的同步,而多線程的同步本質上就是多線程通信的問題。操作系統裏面定義了幾種進程通信的方式:

1. 管道 pipeline

2. 信號 signal

3. 消息隊列 messsage queue

4. 共享內存 shared memory

5. 信號量 semaphore

6. Socket


Java裏面進行多線程通信的主要方式就是共享內存的方式,共享內存基本的關註點有兩個:可見性和有序性。加上復合操作的原子性。我們能夠覺得Java的線程安全性問題主要關註點有3個

1. 可見性

2. 有序性

3. 原子性


Java內存模型JMM攻克了可見性和有序性的問題,而鎖攻克了原子性的問題。

至於Java內存模型怎樣解決可見性和有序性的問題,以後會說到,感興趣的同學能夠看看上面的資料。


可見性指的是一個線程對變量的寫操作對其它線程興許的讀操作可見。因為現代CPU都有多級緩存,CPU的操作都是基於快速緩存的,而線程通信是基於內存的,這中間有一個Gap, 可見性的關鍵還是在對變量的寫操作之後可以在某個時間點顯示地寫回到主內存,這樣其它線程就能從主內存中看到最新的寫的值。volatile,synchronized, 顯式鎖,原子變量這些同步手段都可以保證可見性。

可見性底層的實現是通過加內存屏障實現的:
1. 寫變量後加寫屏障。保證CPU寫緩沖區的值強制刷新回主內存
2. 讀變量之前加讀屏障。使緩存失效,從而強制從主內存讀取變量最新值
寫volatile變量 = 進入鎖
讀volatile變量 = 釋放鎖


有序性指的是數據不相關的變量在並發的情況下。實際運行的結果和單線程的運行結果是一樣的。不會由於重排序的問題導致結果不可預知。volatile, final, synchronized。顯式鎖都能夠保證有序性。


有序性的語意有幾層,
1. 最常見的就是保證多線程運行的串行順序
2. 防止重排序引起的問題
3. 程序運行的先後順序。比方JMM定義的一些Happens-before規則


重排序的問題是一個單獨的主題。常見的重排序有3個層面:
1. 編譯級別的重排序,比方編譯器的優化
2. 指令級重排序,比方CPU指令運行的重排序
3. 內存系統的重排序,比方緩存和讀寫緩沖區導致的重排序


原子性是指某個(些)操作在語意上是原子的。比方讀操作。寫操作,CAS(compare and set)操作在機器指令級別是原子的,又比方一些復合操作在語義上也是原子的,如先檢查後操作if(xxx == null){}
有個專有名詞競態條件來描寫敘述原子性的問題。
競態條件(racing condition)是指某個操作因為不同的運行時序而出現不同的結果,比方先檢查後操作。
volatile變量僅僅保證了可見性,不保證原子性, 比方a++這樣的操作在編譯後實際是多條語句。比方先讀a的值,再加1操作。再寫操作。運行了3個原子操作,假設並發情況下,另外一個線程非常有可能讀到了中間狀態,從而導致程序語意上的不對。

所以a++實際是一個復合操作。
加鎖能夠保證復合語句的原子性。sychronized能夠保證多條語句在synchronized塊中語意上是原子的。

顯式鎖保證臨界區的原子性。

原子變量也封裝了對變量的原子操作。非堵塞容器也提供了原子操作的接口,比方putIfAbsent。



理解可見性,有序性。原子性是理解並發編程的一個重要基礎


聊聊高並發(十九)理解並發編程的幾種"性" -- 可見性,有序性,原子性