java升序问题_JAVA并发理解之重排序问题
首先我們先來了解一下什么是重排序:重排序是指編譯器和處理器為了優(yōu)化程序性能而對(duì)指令序列進(jìn)行重新排序的一種手段。
從Java源代碼到最終實(shí)際執(zhí)行的指令序列,會(huì)分別經(jīng)歷下面3種重排序,如下圖所示
上述的1屬于編譯器重排序,2和3屬于處理器重排序。這些重排序可能會(huì)導(dǎo)致多線程程序出現(xiàn)內(nèi)存可見性問題。在單線程程序中,對(duì)存在控制依賴的操作重排序,不會(huì)改變執(zhí)行結(jié)果(這也是as-if-serial語義允許對(duì)存在控制依賴的操作做重排序的原因);但在多線程程序中,對(duì)存在控制依賴的操作重排序,可能會(huì)改變程序的執(zhí)行結(jié)果。
1)數(shù)據(jù)依賴性(針對(duì)單個(gè)處理器而已)
關(guān)于重排序,這里要先講一個(gè)概念就是數(shù)據(jù)依賴性問題。如果兩個(gè)操作訪問同一個(gè)變量,且這兩個(gè)操作中有一個(gè)為寫操作,此時(shí)這兩個(gè)操作之間就存在數(shù)據(jù)依賴性。數(shù)據(jù)依賴分為下列3種類型,如下表所示。
上面3種情況,只要重排序兩個(gè)操作的執(zhí)行順序,程序的執(zhí)行結(jié)果就會(huì)被改變。前面提到過,編譯器和處理器可能會(huì)對(duì)操作做重排序。編譯器和處理器在重排序時(shí),會(huì)遵守?cái)?shù)據(jù)依賴性,編譯器和處理器不會(huì)改變存在數(shù)據(jù)依賴關(guān)系的兩個(gè)操作的執(zhí)行順序。這里所說的數(shù)據(jù)依賴性僅針對(duì)單個(gè)處理器中執(zhí)行的指令序列和單個(gè)線程中執(zhí)行的操作,不同處理器之間和不同線程之間的數(shù)據(jù)依賴性不被編譯器和處理器考慮。
2)as-if-serial語義
as-if-serial語義的意思是:不管怎么重排序,(單線程)程序的執(zhí)行結(jié)果不能被改變。編譯器、runtime和處理器都必須遵守as-if-serial語義。
為了遵守as-if-serial語義,編譯器和處理器不會(huì)對(duì)存在數(shù)據(jù)依賴關(guān)系的操作做重排序。as-if-serial語義把單線程程序保護(hù)了起來,as-if-serial語義使單線程程序員無需擔(dān)心重排序會(huì)干擾他們,也無需擔(dān)心內(nèi)存可見性問題。
3)happens-before
如果一個(gè)操作執(zhí)行的結(jié)果需要對(duì)另一個(gè)操作可見,那么這兩個(gè)操作之間必須要存在happens-before關(guān)系。這里提到的兩個(gè)操作既可以是在一個(gè)線程之內(nèi),也可以是在不同線程之間。
對(duì)happens-before關(guān)系的具體定義如下。
① 如果一個(gè)操作happens-before另一個(gè)操作,那么第一個(gè)操作的執(zhí)行結(jié)果將對(duì)第二個(gè)操作可見,而且第一個(gè)操作的執(zhí)行順序排在第二個(gè)操作之前。
②兩個(gè)操作之間存在happens-before關(guān)系,并不意味著Java平臺(tái)的具體實(shí)現(xiàn)必須要按照?happens-before關(guān)系指定的順序來執(zhí)行。如果重排序之后的執(zhí)行結(jié)果,與按happens-before關(guān)系來執(zhí)行的結(jié)果一致,那么這種重排序并不非法(也就是說,JMM允許這種重排序)。
上面的①是JMM對(duì)程序員的承諾。從程序員的角度來說,可以這樣理解happens-before關(guān)系:如果A happens-before B,那么Java內(nèi)存模型將向程序員保證——A操作的結(jié)果將對(duì)B可見,且A的執(zhí)行順序排在B之前。注意,這只是Java內(nèi)存模型向程序員做出的保證!上面的②是JMM對(duì)編譯器和處理器重排序的約束原則。正如前面所言,其實(shí)是在遵循一個(gè)基本原則:只要不改變程序的執(zhí)行結(jié)果(指的是單線程程序和正確同步的多線程程序),編譯器和處理器怎么優(yōu)化都行。因此,happens-before關(guān)系本質(zhì)上和as-if-serial語義是一回事。
·as-if-serial語義保證單線程內(nèi)程序的執(zhí)行結(jié)果不被改變,happens-before關(guān)系保證正確同步的多線程程序的執(zhí)行結(jié)果不被改變。
·as-if-serial語義給編寫單線程程序的程序員創(chuàng)造了一個(gè)幻境:單線程程序是按程序的順序來執(zhí)行的。happens-before關(guān)系給編寫正確同步的多線程程序的程序員創(chuàng)造了一個(gè)幻境:正確同步的多線程程序是按happens-before指定的順序來執(zhí)行的。
·?as-if-serial語義和happens-before這么做的目的,都是為了在不改變程序執(zhí)行結(jié)果的前提下,盡可能地提高程序執(zhí)行的并行度。
happens-before規(guī)則如下:
程序順序規(guī)則:一個(gè)線程中的每個(gè)操作,happens-before于該線程中的任意后續(xù)操作。
監(jiān)視器鎖規(guī)則:對(duì)一個(gè)鎖的解鎖,happens-before于隨后對(duì)這個(gè)鎖的加鎖。
volatile變量規(guī)則:對(duì)一個(gè)volatile域的寫,happens-before于任意后續(xù)對(duì)這個(gè)volatile域的讀。
傳遞性:如果A happens-before B,且B happens-before C,那么A happens-before C。
start()規(guī)則:如果線程A執(zhí)行操作ThreadB.start()(啟動(dòng)線程B),那么A線程的ThreadB.start()操作happens-before于線程B中的任意操作。
join()規(guī)則:如果線程A執(zhí)行操作ThreadB.join()并成功返回,那么線程B中的任意操作happens-before于線程A從ThreadB.join()操作成功返回。
總結(jié)
以上是生活随笔為你收集整理的java升序问题_JAVA并发理解之重排序问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 85度c加盟费大概要多少
- 下一篇: java图片序列化_Java中的强大武器