在寫線程池的時候,遇到了很多的問題,特別是happen-before應該怎么去理解,怎么去利用,還有reorder,哪些操作有可能會被reorder?在這一點上,發現其實《concurrent in practice》也沒描述得太清晰。
在網上搜了一遍,發現JSR133的faq相對而言,還算稍微解釋了一下,發現JSR133其實也就40多頁,所以也就順帶看了一遍,因為大部分的內容都比較簡單(越往后看發現越復雜~),但是里面的定義比較難理解,所以只記錄了定義和一些個人認為比較重要的地方,或者比較難理解的地方
這里是閱讀筆記,以備以后查閱
前言部分
1.下面的網站提供了附加的信息,幫助進一步地理解JSR133
http://www.cs.umd.edu/~pugh/java/memoryModel/
2.在JLS中很可能需要JVM(TM)實現的兩個原始的定義的改變:
一.introduction
3.JSR133并不是描述多線程程序應該怎么去執行,而是描述多線程程序允許怎么去顯示的,他包括一些規則,這些規則定義了一個被多線程更新的共享變量的值,是否是可見的。(比如讀一個共享變量的值,根據規則,應該顯示的是什么)
4.還是synchronized的問題,該問題在之前的文章中也提到過,在方法上的時候,它鎖的是this,如果是靜態方法,那么鎖的是方法所在類的class
二.Incorrenctly Synchronized Programs Exhibit Surprising Behaviors
5.不合適的同步(improperly synchronized):(注:并不意味著錯誤)
三.Informal Semantics
6.正確的同步(correct synchronization)(嚴格地保證多線程訪問的正確性,但吞吐率很低的)
理解一個程序是否被正確地同步,有兩個關鍵點:
這里有一個很有意思的圖片,可能對別人沒太多的意義,但對我自己而言,真是解釋了不少以前的迷惑,最主要是第二幅圖片的,之前有一個誤解:數據只有在同步塊中,而另一個線程必須也進入一個相同的鎖的同步塊中,才能保證其可見性。但其實不是這樣的,只要能夠保證順序一致性,就是可見的,如圖a
7.final field
在這一節,并沒有解釋final field是怎么保證其同步的(根據之前的理解,如果有final field,應該在對象的構造完成之后做了一些事來保證同步,但到底做了什么事?還得看下文第9章),只是定義了final field在構造完成后,就不會再改變,所以只需要在構造器中,保證其沒有escape,就可以正確地在并發環境中無限制地使用了。
四.What is a Memory Model?
8.存儲模型(memory model)的定義:
一個存儲模型(memory model)描述的是,給定一個程序和這個程序的執行軌跡(trace),就可以判斷這個執行軌跡(trace)是否是合法的。在Java程序語言中,存儲模型(memory model)是這樣工作的:檢查在運行軌跡(trace)中的每個讀,并且根據一定的規則,校驗這個讀觀察到的寫是否是有效的。
存儲模型(memory model)描述一個程序可能的舉止。一個存儲模型(memory model)實現可以隨意地產生任何的代碼(像reorder,刪除一些沒必要的同步),只要所有的程序運行結果可以根據存儲模型(memory model)來預測。
9.JVM做的一些事
當我們說讀(read),我們只是說的是一些這樣的動作:如讀一些fields或者數組。其他操作的語義,像讀一個數組的長度,執行一個類型轉換(checked casts),或者調用一個虛擬的方法(invocations of virtual methodds,個人的理解應該是調用接口的一個方法),是不會直接受數據競爭的影響的。JVM的實現保證了數據競爭不會導致錯誤的舉動,像返回一個錯誤的數組長度,或者調用虛擬方法的錯誤(在競爭的數據中,應該是可能發生的)。
五.Definitions
10.共享變量/堆內存(Shared variables/Heap memory):
能夠在線程間被共享的內存被叫做共享內存或者堆內存。所有的實例域(instance fields),靜態域(static fileds)和數組都被存在堆內存。我們用變量(variable)來引用所有的域和數組。在一個方法中的本地變量不會在線程中共享,也不會受存儲模型(memory model)影響。
11.線程交互動作(Inter-thread Actions):
一個線程交互動作(inter-thread action)是這樣的動作:它在一個線程中被調用,可以被其他線程檢測或者直接影響。inter-thread actions包括讀和寫共享變量和同步動作,像lock或者unlock,讀或者寫一個volatile變量,和開始一個線程。同時,也包括一些與外部世界交互的動作(external actions),和可以導致一個線程進入無限循環的動作(thread divergence actions)。
每一個線程交互動作(inter-thread action)都是與這個動作相關的信息聯系在一起的。所有的動作都與它被調用的線程聯系在一起,并且和線程中的程序順序(Program order)聯系在一起。附加的聯系信息包括:
write The variable written to and the value written.
read The variable read and the write seen (from this, we can determine
the value seen).
lock The monitor which is locked.
unlock The monitor which is unlocked.
12.程序順序(Program order):
根據線程內語義(intra-thread semantics),包括所有的線程t中的線程交互動作(inter-hread actions),和所有的動作在內,t的程序順序(program order)是唯一影響執行順序的。(這個定義應該是說在線程中,表現出來的執行順序永遠是一致的,即使某些動作與其他線程有交互)
13.線程內語義(Intra-thread semantics):
線程內語義(Intra-thread semantics)是一個單線程程序的基本語義,它允許根據在線程中讀操作看到的值對線程的動作的完整的預測。為了確定線程中的動作是否是合法的,我們只是簡單地評估在單線程中它的正確性,像定義在JLS中的。
每次線程t的評估產生一個線程交互動作(inter-thread action),它必須匹配這個程序順序中的下一個動作a。如果a是一個讀操作,那么進一步的評估會根據存儲模型(memory model)并使用看到的a的值做決定。
簡單地說:線程內語義(intra-thread semantics)決定了在一個單獨的線程中的執行。當值是從堆里面讀出來的,他們就由存儲模型(memory model)來決定
14.同步動作(Synchronization Actions):
同步動作包括lock,unlock,對volatile變量的讀和寫,開始一個線程的動作start,檢測一個線程是否結束的動作join等。任何在一個synchronizes-with邊緣(edge),包括開始和結束點,都是一個同步動作(synchronization action)。這樣的動作會在后面的happens-before邊緣(edge)更詳細地列出來
15.同步順序(Synchronization order):
每個執行有一個同步順序(怎么理解?)。一個同步順序是包括所有的在該執行中的同步動作的所有順序。(也不是太理解)
16.Happens-Before and Synchronizes-With Edges:
同步動作(synchronized action)也會引發happen-before邊緣,我們將結果指向邊緣(directed edges)叫做synchroized-with edges。他們定義在下面:
17.happen-before規則的稍微詳細的解釋:
注意這一點:兩個動作間happens-before關系的存在,并不意味著他們在實現上也按這個順序來執行。如果一個重排序產生的結果與合法的操作一致,它就不是非法的。舉個例子:對一個對象中所有變量(field)默認值的寫操作并不一定要發生在線程開始后的動作之前,只要沒有任何的讀操作觀察到這個事實。(也就是說,如果沒有操作去讀,即使有happen-before關系,也是允許重排序的,這其實也解釋了為什么我們在其他線程中能觀察到該線程的亂序操作,因為在本線程中雖然有重排序操作,但是沒有讀操作,所以允許它重排序)
進一步說,如果兩個操作享有一個happens-before的關系,另一個沒有享有該happen-before關系的動作,不一定能觀察到他們這個happen-before的順序,有可能仍然是亂序的。
六.Approximations to a Java Memory Model
這一章看了幾遍,感覺還是有點模糊,大概的意思算是有點理解了(如果有偏差,還請指出)
18.Happens-Before Memory Model
在定義JMM之前,先定義一個能滿足JMM的所有要求,但還存在因果循環問題(causal loops,即因為A得出B,因為B得出C,因為C得出D,因為D得出A,因為這整條鏈沒有一個起因,而實際上又會發生,這就叫因果循環問題)的一個模型。
對這個模型的描述在這里就不翻譯了,很簡單,也很模糊,翻譯肯定翻譯不清楚的。
這里就列舉一下該模型存在的問題的例子:(這些問題都是由對模型的定義推出的合法的而且是正確的問題,但可以看到存在明顯的因果循環,我們是不能接受的)
在Happens-Before Memory Model中,會出現這樣的結果x=y=1
在Happens-Before Memory Model中,JVM允許這樣的優化,所以也會導致因果循環的問題
很明顯,我們應該允許寫操作的提前提交,以得到效能優化的效果(如上圖的右圖),但有些操作應該不允許的。不正式地說:當涉及到數據競爭的時候,就不應該允許提前提交,否則,就是允許的。
七.Formal Specification of the Java Memory Model
這里就只總結一下非常概括性的內容吧(因為這些定義都沒有例子,難以理解,先看了下文再回來詳細理解這里的定義)
19.JMM的正式的定義
定義動作和執行:
用一個元組定義動作a的概念
用另一個元組定義一個執行的概念
然后對一些動作進行了定義:
外部動作(external actions):略
線程分歧動作(thread divergence action):略
synchronizes-with:略
happens-before略
足夠同步邊緣(sufficient synchronization edges)略
偏序和功能的限制(Restrictions of partial orders and functions)略
設計良好的執行(Well-Formed Executions):
對執行的因果關系的需求(即解決上文所說的因果循環問題的需求):
這是一個推導過程,需要滿足9個條件,就可以推出這個結論,全是數學公式過程,在這里就不抄了(下文的例子貌似就是用這些條件作為依據推理的,看了下文后,再看這里應該能清晰點)
能被觀察到的舉動:略
永不終止的執行:略
八.Illustrative Test Cases and Behaviors
20.以下圖片中出現的情況都是合法的
至于為什么合法,有一定的解釋:因為他可以滿足上文中的“對執行的因果關系的需求”中的9個條件
很明顯,將y=1提前了,是允許的(文章中有根據條件對提交步驟的推理,證明是可行的,之后的例子都沒有,如果時間空閑,后面將試著補充推導過程)
也是一個指令的重排序,但注意:r1==1或者r2==2是不允許的:如果寫提前了,他們對本地的讀就會看不到這個寫。
編譯器會優化這個==操作,變成if(true),所以將b=2提前了,也是允許的
編譯器會發現分支無論怎么走,都會執行a=1,所以也提前了
編譯器會發現r1要么為1要么為0,所以r2必然=1,所以也提前了
這個現象感覺實在沒辦法解釋了,文中的解釋感覺也不通~~
翻譯如下:
編譯器會發現,唯一有可能分配到x的值是0或者42。根據這個,編譯器可以推斷:當執行到r1=x,要么剛好執行了一個寫x=42,要么剛好讀x,并且看到了值42(為什么?根本解釋不通的~)。無論是哪種情況,一個對x的讀看到了值42,然后它會將r1=x改為r1=42,這也會允許y=r1轉變為y=42,并且提早發生了,這樣就出現了描述的情況:r1=r2=r3=42
21.以下圖片中的情況是不合法的
但解釋挺牽強的,說是由于安全的原因,比如在第二個圖中,如果42是對某個對象的引用,而這個引用是Thread4持有的,他打算只有當z=1時,才讓Thread1和Thread2看到。
如果發生了tu2的情況,安全就沒有保證了,所以不允許~~
九.Final Field Semantics
22.final filed的需求和目標final field的值將不會改變
只包含final filed的對象在“成功構造”(注意,這是關鍵的必要條件,也就是在構造成功之后,this才能被別的線程看到)結束后,在可能有競爭的線程間傳遞,需要被認為是不變的。
對final field的讀,將最小化編譯器/結構的消耗(cost)(相對于非final field)
final field的語義需要允許某些場景(如反序列化),在這些場景中,對象中的final field允許構造結束后被修改(順便瀏覽了一下反序列化的源碼(見以下代碼),還真是先創建了對象,然后才讀入field的值的,以后要是面試面到了final field,又可以忽悠一下)
23.final field safe context
為了防止final field在構造器中的設值與發生在之后的讀的重排序(reorder),定義了一個叫final field safe context的區域。
文章是這樣解釋的:如果一個對象在final field safe context中被構造(注意,這是必要條件,如果是在final field safe context中,但對象沒有被構造,也就沒有該限制),對該對象的final field的值的讀,將不會與發生在final field safe context中的寫進行重排序。
舉個例子:像在clone方法和ObjectInputStream.readObject方法中使用這樣一個final field safe context就不會出現上述重排序的問題了(這些例子都是構造成功后修改final field的值的情況)。(但這個final field safe context怎么加?如果我們要人為地利用這個東西,怎么去編碼呢?文章沒有提及,而瀏覽了一下clone和readObject方法,也沒有調用相應的本地方法,那這個final field safe context是如何被調用的呢?方法覆蓋?)
24.final field的正式語義
因為比較重要,這也貼一下原文,以防被小弟誤譯了
A freeze action on a final field f of an object o takes place when a constructor for o in which f is written exits,either abruptly or normally
如果一個對象o的包含對final field的寫操作的構造方法發生,一個對final field的一個固化操作將會被調用,即使這個構造方法可以是突然的,或者正常的。
進一步的解釋:
一些特殊的機制像反射,反序列化,是允許在構造結束后修改final field的值的。像可以使用java.lang.reflect中的setX(...)方法來達到這個效果。當這個fiedl是final的,這個方法只有滿足2個條件才能設置成功,否則會拋IllegalAccessException異常:這個field不是靜態的,并且對這個field設置setAccessible(true)成功。
已有 0 人發表留言,猛擊->>這里<<-參與討論
ItEye推薦
在網上搜了一遍,發現JSR133的faq相對而言,還算稍微解釋了一下,發現JSR133其實也就40多頁,所以也就順帶看了一遍,因為大部分的內容都比較簡單(越往后看發現越復雜~),但是里面的定義比較難理解,所以只記錄了定義和一些個人認為比較重要的地方,或者比較難理解的地方
這里是閱讀筆記,以備以后查閱
前言部分
1.下面的網站提供了附加的信息,幫助進一步地理解JSR133
http://www.cs.umd.edu/~pugh/java/memoryModel/
2.在JLS中很可能需要JVM(TM)實現的兩個原始的定義的改變:
- volatile變量的語義被加強了,以前的語義是允許自由地被reorder的
- final的語義也被加強了,現在可以不需要顯性得同步,就可以獲得線程安全的不變性。這可能需要在含有設置final field的構造函數的結尾,加入一些存儲屏障的步驟
一.introduction
3.JSR133并不是描述多線程程序應該怎么去執行,而是描述多線程程序允許怎么去顯示的,他包括一些規則,這些規則定義了一個被多線程更新的共享變量的值,是否是可見的。(比如讀一個共享變量的值,根據規則,應該顯示的是什么)
4.還是synchronized的問題,該問題在之前的文章中也提到過,在方法上的時候,它鎖的是this,如果是靜態方法,那么鎖的是方法所在類的class
二.Incorrenctly Synchronized Programs Exhibit Surprising Behaviors
5.不合適的同步(improperly synchronized):(注:并不意味著錯誤)
- 一個線程寫
- 另一個線程讀
- 讀和寫沒有用synchronized來保證順序
三.Informal Semantics
6.正確的同步(correct synchronization)(嚴格地保證多線程訪問的正確性,但吞吐率很低的)
理解一個程序是否被正確地同步,有兩個關鍵點:
- 沖突的訪問(Conflicting Accesses):對同一個共享域或共享數組的多個訪問,并且這些訪問至少有一個是寫,就說明有沖突。
- Happens-Before關系 :如果一個動作happens-before另一個,前者對后者是可見的,并且在執行順序上也會在后者的前面。 這點必須強調一下:一個happens-before關系,并不是暗示這些動作在java平臺實現中,必須按這樣的順序去執行。(這里并不是很理解,難道也會是一個幻象?)原話是這樣的:It should be stressed that a happens-before relationship between two actions does not imply that those actions must occur in that order in a Java platform implementation.
Happens-before關系最主要是強調并定義了兩個有競爭的動作之間的順序(當數據競爭出現時)
happens-before的規則包括:(注意,這些動作在直覺上是本該如此的,但在多線程中,就不一定了,所以才有這些規則)
a.在這個線程中,每個動作happens-before每個之后的動作。
b.一個對固有鎖(monitor)的unlock操作happens-before每個之后的對monitor的lock操作。
c.一個對volatile filed的寫happens-before每個之后的對該volatile的讀(注意,這里沒有線程的限制)
d.一個對線程的start()的調用(call)happens-before該線程中任何動作。
e.在線程中的所有動作happens-before任何其他線程成功地調用對該線程的join()返回
f.如果a happens-before b,b happens-before c,那么a happens-before c,即具有傳遞性
這里有一個很有意思的圖片,可能對別人沒太多的意義,但對我自己而言,真是解釋了不少以前的迷惑,最主要是第二幅圖片的,之前有一個誤解:數據只有在同步塊中,而另一個線程必須也進入一個相同的鎖的同步塊中,才能保證其可見性。但其實不是這樣的,只要能夠保證順序一致性,就是可見的,如圖a

7.final field
在這一節,并沒有解釋final field是怎么保證其同步的(根據之前的理解,如果有final field,應該在對象的構造完成之后做了一些事來保證同步,但到底做了什么事?還得看下文第9章),只是定義了final field在構造完成后,就不會再改變,所以只需要在構造器中,保證其沒有escape,就可以正確地在并發環境中無限制地使用了。
四.What is a Memory Model?
8.存儲模型(memory model)的定義:
一個存儲模型(memory model)描述的是,給定一個程序和這個程序的執行軌跡(trace),就可以判斷這個執行軌跡(trace)是否是合法的。在Java程序語言中,存儲模型(memory model)是這樣工作的:檢查在運行軌跡(trace)中的每個讀,并且根據一定的規則,校驗這個讀觀察到的寫是否是有效的。
存儲模型(memory model)描述一個程序可能的舉止。一個存儲模型(memory model)實現可以隨意地產生任何的代碼(像reorder,刪除一些沒必要的同步),只要所有的程序運行結果可以根據存儲模型(memory model)來預測。
9.JVM做的一些事
當我們說讀(read),我們只是說的是一些這樣的動作:如讀一些fields或者數組。其他操作的語義,像讀一個數組的長度,執行一個類型轉換(checked casts),或者調用一個虛擬的方法(invocations of virtual methodds,個人的理解應該是調用接口的一個方法),是不會直接受數據競爭的影響的。JVM的實現保證了數據競爭不會導致錯誤的舉動,像返回一個錯誤的數組長度,或者調用虛擬方法的錯誤(在競爭的數據中,應該是可能發生的)。
五.Definitions
10.共享變量/堆內存(Shared variables/Heap memory):
能夠在線程間被共享的內存被叫做共享內存或者堆內存。所有的實例域(instance fields),靜態域(static fileds)和數組都被存在堆內存。我們用變量(variable)來引用所有的域和數組。在一個方法中的本地變量不會在線程中共享,也不會受存儲模型(memory model)影響。
11.線程交互動作(Inter-thread Actions):
一個線程交互動作(inter-thread action)是這樣的動作:它在一個線程中被調用,可以被其他線程檢測或者直接影響。inter-thread actions包括讀和寫共享變量和同步動作,像lock或者unlock,讀或者寫一個volatile變量,和開始一個線程。同時,也包括一些與外部世界交互的動作(external actions),和可以導致一個線程進入無限循環的動作(thread divergence actions)。
每一個線程交互動作(inter-thread action)都是與這個動作相關的信息聯系在一起的。所有的動作都與它被調用的線程聯系在一起,并且和線程中的程序順序(Program order)聯系在一起。附加的聯系信息包括:
write The variable written to and the value written.
read The variable read and the write seen (from this, we can determine
the value seen).
lock The monitor which is locked.
unlock The monitor which is unlocked.
12.程序順序(Program order):
根據線程內語義(intra-thread semantics),包括所有的線程t中的線程交互動作(inter-hread actions),和所有的動作在內,t的程序順序(program order)是唯一影響執行順序的。(這個定義應該是說在線程中,表現出來的執行順序永遠是一致的,即使某些動作與其他線程有交互)
13.線程內語義(Intra-thread semantics):
線程內語義(Intra-thread semantics)是一個單線程程序的基本語義,它允許根據在線程中讀操作看到的值對線程的動作的完整的預測。為了確定線程中的動作是否是合法的,我們只是簡單地評估在單線程中它的正確性,像定義在JLS中的。
每次線程t的評估產生一個線程交互動作(inter-thread action),它必須匹配這個程序順序中的下一個動作a。如果a是一個讀操作,那么進一步的評估會根據存儲模型(memory model)并使用看到的a的值做決定。
簡單地說:線程內語義(intra-thread semantics)決定了在一個單獨的線程中的執行。當值是從堆里面讀出來的,他們就由存儲模型(memory model)來決定
14.同步動作(Synchronization Actions):
同步動作包括lock,unlock,對volatile變量的讀和寫,開始一個線程的動作start,檢測一個線程是否結束的動作join等。任何在一個synchronizes-with邊緣(edge),包括開始和結束點,都是一個同步動作(synchronization action)。這樣的動作會在后面的happens-before邊緣(edge)更詳細地列出來
15.同步順序(Synchronization order):
每個執行有一個同步順序(怎么理解?)。一個同步順序是包括所有的在該執行中的同步動作的所有順序。(也不是太理解)
16.Happens-Before and Synchronizes-With Edges:
同步動作(synchronized action)也會引發happen-before邊緣,我們將結果指向邊緣(directed edges)叫做synchroized-with edges。他們定義在下面:
- 一個unlock動作synchronizes-with所有之后的在同一個鎖上的lock動作(這里“之后的”的含義根據synchronization order定義)
- 一個對volatile變量的寫synchronizes-with所有之后的對該變量的讀操作(任何線程)(這里“之后的”的含義根據synchronization order定義)
- 一個開始一個線程的動作synchronizes-with線程開始后的第一個操作
- 線程T1的最后一個動作synchronizes-with另一個線程T2檢測到T1已經停止后的任何動作。T2可以通過調用T1.isAlive()或者join動作來完成這個操作。
- 如果線程T1中斷(interrupts)線程T2,T1的interrupt動作synchronizes-with任何其他的線程(包括T2)檢測到T2被中斷interrupted。
- 對每個變量的默認值的寫synchronizes-with每個線程的第一個動作。
- 對一個對象的finalizer的調用,有一個隱性的對該對象引用的讀操作。在對象的構造器的結尾和這個讀操作之間,有一個happens-before edge。 注意:所有的對這個對象的凍結(freezes)happen-before這個happens-before edge的開始點。(這段比較難理解,在9.2會重新提及,暫時先記錄下來)
17.happen-before規則的稍微詳細的解釋:
注意這一點:兩個動作間happens-before關系的存在,并不意味著他們在實現上也按這個順序來執行。如果一個重排序產生的結果與合法的操作一致,它就不是非法的。舉個例子:對一個對象中所有變量(field)默認值的寫操作并不一定要發生在線程開始后的動作之前,只要沒有任何的讀操作觀察到這個事實。(也就是說,如果沒有操作去讀,即使有happen-before關系,也是允許重排序的,這其實也解釋了為什么我們在其他線程中能觀察到該線程的亂序操作,因為在本線程中雖然有重排序操作,但是沒有讀操作,所以允許它重排序)
進一步說,如果兩個操作享有一個happens-before的關系,另一個沒有享有該happen-before關系的動作,不一定能觀察到他們這個happen-before的順序,有可能仍然是亂序的。
六.Approximations to a Java Memory Model
這一章看了幾遍,感覺還是有點模糊,大概的意思算是有點理解了(如果有偏差,還請指出)
18.Happens-Before Memory Model
在定義JMM之前,先定義一個能滿足JMM的所有要求,但還存在因果循環問題(causal loops,即因為A得出B,因為B得出C,因為C得出D,因為D得出A,因為這整條鏈沒有一個起因,而實際上又會發生,這就叫因果循環問題)的一個模型。
對這個模型的描述在這里就不翻譯了,很簡單,也很模糊,翻譯肯定翻譯不清楚的。
這里就列舉一下該模型存在的問題的例子:(這些問題都是由對模型的定義推出的合法的而且是正確的問題,但可以看到存在明顯的因果循環,我們是不能接受的)

在Happens-Before Memory Model中,會出現這樣的結果x=y=1

在Happens-Before Memory Model中,JVM允許這樣的優化,所以也會導致因果循環的問題
很明顯,我們應該允許寫操作的提前提交,以得到效能優化的效果(如上圖的右圖),但有些操作應該不允許的。不正式地說:當涉及到數據競爭的時候,就不應該允許提前提交,否則,就是允許的。
七.Formal Specification of the Java Memory Model
這里就只總結一下非常概括性的內容吧(因為這些定義都沒有例子,難以理解,先看了下文再回來詳細理解這里的定義)
19.JMM的正式的定義
定義動作和執行:
用一個元組定義動作a的概念

用另一個元組定義一個執行的概念

然后對一些動作進行了定義:
外部動作(external actions):略
線程分歧動作(thread divergence action):略
synchronizes-with:略
happens-before略
足夠同步邊緣(sufficient synchronization edges)略
偏序和功能的限制(Restrictions of partial orders and functions)略
設計良好的執行(Well-Formed Executions):
- 每個對變量x的讀操作看到一個對x變量的寫操作(注意,不是說前一個),所有對volatile變量的讀和寫都是volatile操作(沒理解好~)
- 同步順序(Synchronization order)與程序順序(program order)和互斥(mutual exclusion)保持一致
- 執行遵守線程內(intra-thread語義,即在線程內與程序順序一致)一致性
- 執行遵守同步順序(synchronization-order,這個也沒理解好)一致性
- 執行遵守同步順序(happens-before)一致性
對執行的因果關系的需求(即解決上文所說的因果循環問題的需求):
這是一個推導過程,需要滿足9個條件,就可以推出這個結論,全是數學公式過程,在這里就不抄了(下文的例子貌似就是用這些條件作為依據推理的,看了下文后,再看這里應該能清晰點)
能被觀察到的舉動:略
永不終止的執行:略
八.Illustrative Test Cases and Behaviors
20.以下圖片中出現的情況都是合法的
至于為什么合法,有一定的解釋:因為他可以滿足上文中的“對執行的因果關系的需求”中的9個條件

很明顯,將y=1提前了,是允許的(文章中有根據條件對提交步驟的推理,證明是可行的,之后的例子都沒有,如果時間空閑,后面將試著補充推導過程)

也是一個指令的重排序,但注意:r1==1或者r2==2是不允許的:如果寫提前了,他們對本地的讀就會看不到這個寫。

編譯器會優化這個==操作,變成if(true),所以將b=2提前了,也是允許的

編譯器會發現分支無論怎么走,都會執行a=1,所以也提前了

編譯器會發現r1要么為1要么為0,所以r2必然=1,所以也提前了

這個現象感覺實在沒辦法解釋了,文中的解釋感覺也不通~~
翻譯如下:
編譯器會發現,唯一有可能分配到x的值是0或者42。根據這個,編譯器可以推斷:當執行到r1=x,要么剛好執行了一個寫x=42,要么剛好讀x,并且看到了值42(為什么?根本解釋不通的~)。無論是哪種情況,一個對x的讀看到了值42,然后它會將r1=x改為r1=42,這也會允許y=r1轉變為y=42,并且提早發生了,這樣就出現了描述的情況:r1=r2=r3=42
21.以下圖片中的情況是不合法的


但解釋挺牽強的,說是由于安全的原因,比如在第二個圖中,如果42是對某個對象的引用,而這個引用是Thread4持有的,他打算只有當z=1時,才讓Thread1和Thread2看到。
如果發生了tu2的情況,安全就沒有保證了,所以不允許~~
九.Final Field Semantics
22.final filed的需求和目標
Object curObj = curContext.getObj(); ObjectStreamClass curDesc = curContext.getDesc(); bin.setBlockDataMode(false); defaultReadFields(curObj, curDesc);
23.final field safe context
為了防止final field在構造器中的設值與發生在之后的讀的重排序(reorder),定義了一個叫final field safe context的區域。
文章是這樣解釋的:如果一個對象在final field safe context中被構造(注意,這是必要條件,如果是在final field safe context中,但對象沒有被構造,也就沒有該限制),對該對象的final field的值的讀,將不會與發生在final field safe context中的寫進行重排序。
舉個例子:像在clone方法和ObjectInputStream.readObject方法中使用這樣一個final field safe context就不會出現上述重排序的問題了(這些例子都是構造成功后修改final field的值的情況)。(但這個final field safe context怎么加?如果我們要人為地利用這個東西,怎么去編碼呢?文章沒有提及,而瀏覽了一下clone和readObject方法,也沒有調用相應的本地方法,那這個final field safe context是如何被調用的呢?方法覆蓋?)
24.final field的正式語義
因為比較重要,這也貼一下原文,以防被小弟誤譯了
A freeze action on a final field f of an object o takes place when a constructor for o in which f is written exits,either abruptly or normally
如果一個對象o的包含對final field的寫操作的構造方法發生,一個對final field的一個固化操作將會被調用,即使這個構造方法可以是突然的,或者正常的。
進一步的解釋:
一些特殊的機制像反射,反序列化,是允許在構造結束后修改final field的值的。像可以使用java.lang.reflect中的setX(...)方法來達到這個效果。當這個fiedl是final的,這個方法只有滿足2個條件才能設置成功,否則會拋IllegalAccessException異常:這個field不是靜態的,并且對這個field設置setAccessible(true)成功。
已有 0 人發表留言,猛擊->>這里<<-參與討論
ItEye推薦