java內(nèi)存模型的抽象結(jié)構(gòu)
實例域、靜態(tài)域、數(shù)組元素存儲在堆內(nèi)存,堆內(nèi)存是線程間共享的
局部變量、方法定義參數(shù)、異常處理參數(shù)不會在線程之間共享
java memory model
線程之間的共享內(nèi)存存儲在主內(nèi)存,每個線程都會有個本地的線程副本
重排序
- 編譯器優(yōu)化重排序
- 指令級并行重排序
- 內(nèi)存系統(tǒng)重排序
java內(nèi)存模型之happens-before
jmm的設(shè)計
jmm把happens-before要求禁止的重排序分為了兩類:
- 會改變程序執(zhí)行結(jié)果的重排序
- 不會改變程序執(zhí)行結(jié)果的重排序
jmm會這兩種不同性質(zhì)的重排序采用不同策略:
- 對于會改變程序執(zhí)行結(jié)果的重排序,jmm要求編譯器和處理器必須禁止這種重排序
- 對于不會改變程序執(zhí)行結(jié)果的重排序,jmm沒有要求(即為許可這種重排序)
happens-before的定義
- 如果一個操作happens-before另一個操作,那么第一個操作的執(zhí)行結(jié)果將對第二個操作可見,而且第一個操作的執(zhí)行順序排在第二個操作之前(從編程人員角度來說:如果A happens-before B,那么java內(nèi)存模型將向程序員保證-A操作的結(jié)果將對B可見,且A的執(zhí)行順序排在B之前.注意,這只是java內(nèi)存模型像程序員做出的保證)
- 兩個操作之間存在happens-before關(guān)系,并不意味這java平臺的具體實現(xiàn)必須按照happens-before關(guān)系指定的順序來執(zhí)行.如果重排序之后的執(zhí)行結(jié)果,與按happens-before關(guān)系執(zhí)行的結(jié)果一致,那么是運行這種重排序(JMM其實是遵循一個基本原則:只要不改變程序的執(zhí)行結(jié)果{指的是單線程程序和正確同步的多線程程序},編譯器和處理器怎么優(yōu)化都行.JMM這么做的原因是:程序員對于這兩個操作是否真的被重排序并不關(guān)心,關(guān)心的是程序執(zhí)行時的語義不能被改變{既執(zhí)行結(jié)果不能被改變}.因此,happens-before關(guān)系本質(zhì)上和as-if-serial語義是一回事)。
happens-before規(guī)則
- 程序順序規(guī)則:一個線程中的每個操作,happens-before于該線程中的任意后續(xù)操作
- 監(jiān)視器鎖規(guī)則:對于一個鎖的解鎖,happens-before于隨后對這個鎖的加鎖
- volatile變量規(guī)則:對一個volatile域的寫,happens-before于任意后續(xù)對這個volatile域的讀
- 傳遞性:如果A happens-before B,且B happens-before C,那么A happens-before C。
- start()規(guī)則:如果線程A執(zhí)行操作ThreaB.start()(啟動線程B),那么A線程的 ThreaB.start()操作happens-before于線程B中的任意操作。
- join()規(guī)則:如果線程A執(zhí)行操作ThreaB.join()并成功返回,那么線程B中的任意操作happens-before于線程A從ThreaB.join()操作成功返回。