轉載請注明原創出處,謝謝!
首先需要說說線程安全?關於線程安全一直在提,比如StringBuilder和StringBuffer有什么區別? 經常就會出現關於線程安全與線程非安全,可能一直在提自己沒有細細想想,如果忽然問你啥是線程安全的概念?可能你需要短暫停頓幾秒,線程不安全就是不提供數據訪問保護,有可能出現多個線程先后更改數據造成所得到的數據是臟數據,其實關於線程安全的定義我想不到好的,百度了下,也沒有發現一個特別好的解釋,我就選擇一個相對來說還可以的解釋吧 ,線程安全就是多線程訪問時,采用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。不會出現數據不一致或者數據污染。我覺得該描述也不完全正確,因為現在控制並發的策略很多不僅僅是加鎖機制,也可以不用加鎖,我覺得這樣可能比較合適的解釋,就是多個線程都會操作到的,是一個公共資源或者共享的數據,但是每次操作只能一個線程使用而一旦臨界區資源被占用其他的線程必須等待該資源的釋放,在並行程序中,臨界區資源都是受保護的那么就是線程安全,不包含的就是線程不安全的。
由於並發程序要比串行程序復雜很多,一個最重要的原因就是並發程序下訪問的一致性和安全性將會受到嚴重挑戰,如何保證一個線程可以看到正確的數據呢?因此我們需要深入了解並行機制的前提下,在定義一些規則來保證多線程直接有效的,正確的協同工作,而Java內存模型(JMM)就是來做這些事情的。
JMM模型都是圍繞着多線程的原子性、可見性、和有序性來說的。這塊內容過於復雜,自己水平有些,可能理解的有些偏差,希望到時候大家幫忙指出來。
原子性是指一個操作是不可中斷的,即使在多線程一起執行的時候,一個操作如果開始就不會別其他線程干擾到。原文是:
Atomicity
Accesses and updates to the memory cells corresponding to fields of any type except long or double are guaranteed to be atomic. This includes fields serving as references to other objects. Additionally, atomicity extends to volatile long and double. (Even though non-volatile longs and doubles are not guaranteed atomic, they are of course allowed to be.)
Atomicity guarantees ensure that when a non-long/double field is used in an expression, you will obtain either its initial value or some value that was written by some thread, but not some jumble of bits resulting from two or more threads both trying to write values at the same time. However, as seen below, atomicity alone does not guarantee that you will get the value most recently written by any thread. For this reason, atomicity guarantees per se normally have little impact on concurrent program design.
long型字段和double型字段在32位hotspot可能不是原子性的,該如何證明呢?(作為一個思考題后續章節會進行解答,並且附上程序說明),在64位hotspot下面long型字段和double型字段都是原子性的。如果32位hotspot下volatile long 和volatile double也具有原子性 。為什么在32為hotspot加了volatile long型字段和double型字段字段就一定具有原型性了呢?,這與volatile的特性有關,當我們聲明共享變量為volatile后,對這個變量的讀、寫會很特別,理解volatile的好方法就是把對volatile變量的單個讀寫堪稱使用同一
鎖對這些單個讀寫操作做了同步,鎖的語義決定了臨界區代碼的執行具有原子性,所以在32為hotspot加了volatile long型字段和double型字段字段就一定具有原型性了,后續還會說volatile的,很難理解的關鍵詞。
有序性,如果不是為了優化,為了性能,一般代碼的執行順序就是我們寫的順序從先到后一行一行執行,但是為了提高性能,我們需要優化,可能就會修改這些原先的順序了,
目前的編譯器和處理器常常會對指令做重排的。
- 編譯器優化的重排序。編譯器在不改變單線程語義的前提下,可以重新安排語句的執行順序。
- 指令級並行的重排序。現代處理器采用了指令級並行技術來將多條指令重疊執行。如果不存在數據依賴性,處理器可以改變語句對應機器指令的執行順序。
- 內存系統的重排序。由於處理器換用緩存和讀/寫緩沖區,這使得加載和存儲操作看上去可能是在亂序中執行。上述的1屬於編譯器重排序,2和3屬於處理器重排序。
這些重排序可能會導致多線程程序出現內存可見性問題。對於編譯器,JMM的編譯器重排序規則會禁止特定類型的編譯器重排序(不是所有的編譯器重排序都要禁止)。對於處理器重排序,JMM的處理器重排序規則會要求Java編譯器在生成指令序列時,插入特定類型的內存屏障指令,通過內存屏障指令來禁止特定類型的處理器重排序。關於內存屏障后續等深入了解了在來聊聊。
可見性是指當一個線程修改了某個共享變量的值,其他線程是否能夠立即知道關於這個值的修改,可見性是一個復雜的綜合性的問題,有一些關於緩存優化或者硬件優化會導致可見性的問題之外,上面提到的關於指令重排也會影響到可見性問題。
還有幾個概念介紹:
happens-before簡介
從JDK 5開始,Java使用新的JSR-133內存模型(除非特別說明,本文針對的都是JSR-133內存模型)。JSR-133使用happens-before的概念來闡述操作之間的內存可見性。在JMM中,如果一個操作執行的結果需要對另一個操作可見,那么這兩個操作之間必須要存在happens-before關系。這里提到的兩個操作既可以是在一個線程之內,也可以是在不同線程之間。
與程序員密切相關的happens-before規則如下:
- 程序順序規則:一個線程中的每個操作,happens-before於該線程中的任意后續操作。
- 監視器鎖規則:對一個鎖的解鎖,happens-before於隨后對這個鎖的加鎖。
- volatile變量規則:對一個volatile域的寫,happens-before於任意后續對這個volatile域的讀。
- 傳遞性:如果A happens-before B,且B happens-before C,那么A happens-before C。
注意:兩個操作之間具有happens-before關系,並不意味着前一個操作必須要在后一個操作之前執行!happens-before僅僅要求前一個操作(執行的結果)對后一個操作可見,且前一個操作按順序排在第二個操作之前。happens-before的定義很微妙,一個happens-before規則對應於一個或多個編譯器和處理器重排序規則。對於Java程序員來說,happens-before規則簡單易懂,它避免Java程序員為了理解JMM提供的內存可見性保證而去學習復雜的重排序規則以及這些規則的具體實現方法。
數據依賴性
如果兩個操作訪問同一個變量,且這兩個操作中有一個為寫操作,此時這兩個操作之間就存在數據依賴性。數據依賴分為下列3中類型:
- 寫后讀,a=1;b=a;寫一個變量之后,再讀這個變量。
- 寫后寫,a=1;a=2;寫一個變量之后,再寫這個變量。
- 讀后寫,a=b;b=1;讀一個變量之后,再寫這個變量。
上面3種情況,只要重排序兩個操作的執行順序,程序的執行結果就會被改變。前面提到過,編譯器和處理器可能會對操作做重排序。編譯器和處理器在重排序時,會遵守數據依賴性,編譯器和處理器不會改變存在數據依賴關系的兩個操作的執行順序。
這里所說的數據依賴性僅針對單個處理器中執行的指令序列和單個線程中執行的操作,不同處理器之間和不同線程之間的數據依賴性不被編譯器和處理器考慮。
as-if-serial語義
as-if-serial語義的意思是:不管怎么重排序(編譯器和處理器為了提高並行度),(單線程)程序的執行結果不能被改變。編譯器、runtime和處理器都必須遵守as-if-serial語義。
為了遵守as-if-serial語義,編譯器和處理器不會對存在數據依賴關系的操作做重排序,因為這種重排序會改變執行結果。但是,如果操作之間不存在數據依賴關系,這些操作就可能被編譯器和處理器重排序。
as-if-serial語義把單線程程序保護了起來,遵守as-if-serial語義的編譯器、runtime和處理器共同為編寫單線程程序的程序員創建了一個幻覺:單線程程序是按程序的順序來執行的。as-if-serial語義使單線程程序員無需擔心重排序會干擾他們,也無需擔心內存可見性問題。
關於final和volatile后續章節介紹,很復雜很綜合性的知識,今天這些內容很難理解,希望對大家有所幫助。
個人公眾號

