線程間通信機制有兩種:共享內存、消息傳遞,Java并發(fā)采用的前者(堆內存和線程本地內存見得數(shù)據(jù)同步);
指令重排序
概念:編譯器或處理器為了優(yōu)化程序性能而對指令序列重排序的手段;
從源代碼到最終的指令序列經過了3個重排序:編譯器優(yōu)化重排序(Java編譯器處理)、指令集并行重排序、內存系統(tǒng)重排序(處理器處理);
如何保證內存可見性:通過禁止特定類型的重排序保證可見性和正確性,編譯器通過編譯規(guī)則禁止,處理器通過插入內存屏障處理;
內存屏障指令分為4類:LoadLoad、LoadStore、StoreStore、StoreLoad
happens-before
概念:一個操作執(zhí)行結果對另一個操作可見,則存在happens-before關系。兩個操作可以是一個線程也可以是不同線程的;
并不是一個操作先于一個操作執(zhí)行,而是結果可見且順序上在前面,規(guī)則比如:
程序員層面:happens-before規(guī)則; -> JMM實現(xiàn):禁止特定類型的編譯重排序;-> JMM規(guī)則:編譯器重排序規(guī)則+處理器重排序規(guī)則;
即一個happens-before規(guī)則對應一個或多個編譯器和處理器重排序規(guī)則;
happens-before規(guī)則包括:
程序順序規(guī)則:一個線程中的每個操作happens-before該線程的后續(xù)操作;
volatile規(guī)則:對一個volatile域的寫happens-before任意后續(xù)的讀;
監(jiān)視器鎖規(guī)則:對一個鎖的解鎖happens-before隨后對這個鎖的加鎖;
傳遞性:A?happens-before B,B ?happens-before C,則A?happens-before C;
start()規(guī)則:A線程中執(zhí)行B.start(),則A中的B.start()?happens-before B中的任意操作;
join()規(guī)則:A線程中執(zhí)行B.join(),則B的任意操作happens-before A從B.join()返回;
as-if-serial
概念:不管怎么重排序,單線程執(zhí)行的結果不會被改變;
重而造成單線程按順序執(zhí)行的幻覺,實際上也是經過重排序的,只是不影響結果;
允許控制依賴的重排序:如if(flag){int i=a*a},編譯器可能會先計算好a*a的值放入緩沖區(qū),為true時再將結果寫入i;
as-if-serial保證單線程內程序執(zhí)行結果不被改變,happens-before保證正確同步的多線程執(zhí)行結果不被改變,目的都是在不改變執(zhí)行結果的前提下提升性能;
順序一致性模型
是一個理想的理論參考模型,就是程序的執(zhí)行順序和它編寫的順序一致,特性:一個線程的所有操作必須按照程序的順序執(zhí)行;每個操作都原子且對所有線程立即可見,所有線程看到的總體執(zhí)行順序一致;模型中有一個單一的全局內存,任一時間還能有一個線程連接到內存;
Java沒使用該模型,因為:
編譯器和處理器無法對程序做到優(yōu)化,在Java中我們使用的是可以進行指令重排序的JMM模型。
順序一致模型要求線程的每一個操作都具有原子性,也就是說,讀寫都會操作主存,這樣的效率低;
數(shù)據(jù)依賴性
寫后讀、讀后寫、寫后寫存在數(shù)據(jù)依賴,讀后讀不存在;
編譯器和處理器不會改變存在數(shù)據(jù)依賴的兩個操作;
數(shù)據(jù)依賴的判斷只針對單處理器和單線程,多處理器和多線程不會被編譯器和處理器考慮,所以存在并發(fā)問題;
同步的3個源語:volatile、鎖、final
volatile
volatile變量的單個讀或寫等價于一個普通變量的讀或寫使用同一個鎖同步操作,volatile變量的特性:
可見性:對volatile變量的讀總是能看到任意線程對他最后的寫;
原子性:單個volatile變量的讀或寫具有原子性,類似volatile++的符合操作沒有;(這也是不能替代鎖的原因)
volatile寫的內存語義:JMM把該線程對應的本地內存的變量值刷新到主內存;
volatile讀的內存語義:JMM把該線程對應的本地內存置為無效,線程將從主內存讀?。?/p>
內存語義的實現(xiàn):在volatile讀寫操作的前后加入不同類型的內存屏障(LoadlLoad、LoadStore等)
鎖
鎖的作用:讓臨界區(qū)互斥執(zhí)行;釋放鎖的線程向獲取同一鎖的線程發(fā)送消息;
釋放鎖的內存語義:JMM把該線程對應的本地內存的變量值刷新到主內存;(同volatile寫)
獲取鎖的內存語義:JMM把該線程對應的本地內存置為無效,線程將從主內存讀??;(同volatile讀)
final
final域對象的讀不能重排序;final域的寫不能重排序到構造函數(shù)之外;