理解併發程式設計的幾種"性" -- 可見性,有序性,原子性
轉自:http://blog.sina.com.cn/s/blog_4adc4b090102whzx.html
這篇的主題本應該放在最初的幾篇,討論的是併發程式設計最基礎的幾個核心概念,但是這幾個概念又牽扯到很多的實際技術,比如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 andset)操作在機器指令級別是原子的,又比如一些複合操作在語義上也是原子的,如先檢查後操作if(xxx== null){}
有個專有名詞競態條件來描述原子性的問題。
競態條件(racingcondition)是指某個操作由於不同的執行時序而出現不同的結果,比如先檢查後操作。
volatile變數只保證了可見性,不保證原子性, 比如a++這種操作在編譯後實際是多條語句,比如先讀a的值,再加1操作,再寫操作,執行了3個原子操作,如果併發情況下,另外一個執行緒很有可能讀到了中間狀態,從而導致程式語意上的不正確。所以a++實際是一個複合操作。
加鎖可以保證複合語句的原子性,sychronized可以保證多條語句在synchronized塊中語意上是原子的。
理解可見性,有序性,原子性是理解併發程式設計的一個重要基礎
相關文章
- Java併發之原子性、有序性、可見性Java
- Java併發程式設計-併發程式設計的Bug源頭:可見性、原子性和有序性問題Java程式設計
- Java併發程式設計Bug源頭:可見性、原子性和有序性問題Java程式設計
- 你還不懂可見性、有序性和原子性?
- [深入理解Java虛擬機器]原子性/可見性/有序性Java虛擬機
- Java併發程式設計-解決可見性與有序性問題Java程式設計
- 執行緒安全性-原子性、可見性、有序性執行緒
- 三大性質總結:原子性、可見性以及有序性
- java多執行緒3:原子性,可見性,有序性Java執行緒
- 併發程式設計的原子性 != 事務ACID的原子性程式設計
- 高階java必須清楚的概念:原子性、可見性、有序性Java
- volatile,可見性,有序性
- 深入理解Java多執行緒與併發框(第③篇)——Java記憶體模型與原子性、可見性、有序性Java執行緒記憶體模型
- Java併發程式設計實戰 02Java如何解決可見性和有序性問題Java程式設計
- volatile 可見性與原子性
- 走進volatile的世界,探索它與可見性,有序性,原子性之間的愛恨情仇!
- Java併發程式設計-volatile可見性的介紹Java程式設計
- 「跬步千里」詳解 Java 記憶體模型與原子性、可見性、有序性Java記憶體模型
- 可見性有序性,Happens-before來搞定APP
- 淺談併發的資料競爭(可見性)與競態條件(原子性)
- 併發程式設計基礎 - 管程模型和synchronized原子性程式設計模型synchronized
- 從硬體級別再看可見性和有序性
- 併發程式設計系列之Lock鎖可重入性與公平性程式設計
- java安全編碼指南之:可見性和原子性Java
- 併發bug之源(一)-可見性
- 關於IOS 屬性atomic(原子性)的理解iOS
- 【Java併發入門】02 Java記憶體模型:看Java如何解決可見性和有序性問題Java記憶體模型
- 你知道Java是如何解決可見性和有序性問題的嗎?Java
- 深刻理解JAVA併發中的有序性問題和解決之道Java
- [Java併發]AQS的可重入性JavaAQS
- MVCC - Read View的可見性判斷理解MVCView
- 第10章:併發和分散式程式設計 10.1併發性和執行緒安全性分散式程式設計執行緒
- 面向可複用性和可維護性的設計模式設計模式
- Unsafe原子性
- PostgreSQL vacuum可見性SQL
- .NET併發程式設計-資料結構不可變性程式設計資料結構
- 增強IoT安全和可見性的7種工具 你瞭解幾個?
- 從JDK原始碼角度看併發的原子性如何保證JDK原始碼
- 解密詭異併發問題的幕後黑手:可見性問題解密