????要了解高并發(fā)以及相關(guān)的問題,首先需要了解JAVA內(nèi)存模型花嘶。
????一般來說,線程之間通信的方式包括兩種蹦漠,共享內(nèi)存和消息傳遞椭员。由于多種操作系統(tǒng)存在差異,不同的架構(gòu)系統(tǒng)存在不同的內(nèi)存模型笛园,為了實(shí)現(xiàn)平臺(tái)一致性隘击,JMM應(yīng)運(yùn)而生侍芝,JMM實(shí)際并不真實(shí)存在,本身是種概念埋同,主要解決的是由于多線程通過共享內(nèi)存進(jìn)行通信時(shí)州叠,由于本地內(nèi)存的數(shù)據(jù)不一致或重排序而導(dǎo)致的并發(fā)問題,JMM內(nèi)存模型抽象如下:
????首先解釋一下本地內(nèi)存,實(shí)際上線程本不存在本地內(nèi)存莺禁,而是由計(jì)算機(jī)硬件架構(gòu)導(dǎo)致的留量,在現(xiàn)代多核處理器計(jì)算機(jī)中,為了提升系統(tǒng)的運(yùn)算效率哟冬,處理器并不直接與主內(nèi)存進(jìn)行內(nèi)存的讀寫操作楼熄,而是通過寄存器,高速緩存浩峡,寫緩沖器可岂,無效隊(duì)列等進(jìn)行內(nèi)存的讀寫操作的,而這些部件可以視為線程的本地內(nèi)存翰灾,即主內(nèi)存的副本缕粹。
? ? 其次解釋一下重排序,JAVA代碼從編寫到執(zhí)行纸淮,首選經(jīng)過編譯器的靜態(tài)編譯為class文件平斩,再經(jīng)過JIT編譯器編譯為機(jī)器碼,以及處理器執(zhí)行指令咽块,一般來說靜態(tài)編譯的字節(jié)碼和我們編寫的代碼順序是保持一致的绘面,而編譯器再將字節(jié)碼轉(zhuǎn)換為機(jī)器碼的過程中,出于性能的考慮侈沪,在其認(rèn)為不影響程序正確性的情況下會(huì)執(zhí)行指令重排序揭璃,除此之外,處理器也可能在執(zhí)行過程中進(jìn)行指令重排序亭罪,現(xiàn)代計(jì)算機(jī)為了提升執(zhí)行效率瘦馍,往往不是按照給定順序進(jìn)行逐一執(zhí)行的,而是動(dòng)態(tài)調(diào)整順序应役,指令就緒就先執(zhí)行情组,這樣就會(huì)導(dǎo)致指令的亂序執(zhí)行,另外處理器還有一種猜測執(zhí)行的技術(shù)箩祥,即可能導(dǎo)致if語句優(yōu)先條件語句的執(zhí)行呻惕。而存儲(chǔ)系統(tǒng)也會(huì)導(dǎo)致內(nèi)存重排序。
? ? as-if-serial:該語義即為在單線程下保證代碼在重排序后不影響程序的正確性.這也是JIT編譯器和處理器進(jìn)行重排序需要遵守的規(guī)則滥比,但是并不保證多線程下的代碼正確性。
? ? 內(nèi)存屏障:as-if-serials僅僅保證了單線程下的指令的有序性做院,在多線程下的內(nèi)存重排序與可見性則是通過內(nèi)存屏障來保證的盲泛,在計(jì)算機(jī)中濒持,讀內(nèi)存操作稱之為Load,寫內(nèi)存操作為store寺滚,因此內(nèi)存重排序理論上為四種柑营,LoadStore,LoadLoad,StoreLoad,StoreStore.
? ??Store屏障:強(qiáng)制所有在store屏障指令之前的store指令,都在該store屏障指令執(zhí)行之前被執(zhí)行村视,并把store緩沖區(qū)的數(shù)據(jù)都刷到CPU緩存.
? ? Load屏障:強(qiáng)制所有在load屏障指令之后的load指令官套,都在該load屏障指令執(zhí)行之后被執(zhí)行,并且一直等到load緩沖區(qū)被該CPU讀完才能執(zhí)行之后的load指令蚁孔。
? ? happens-before:該原則保證在多線程的處理下奶赔,一個(gè)操作happens-before另一個(gè)操作,則該操作的結(jié)果一定是可見的杠氢。實(shí)際上并不阻止重排序站刑,在不影響操作結(jié)果的情況下.