(1)多線程共享變量
實例域,靜態域和數組元素都是放在堆內存中(所有線程均可訪問到,是可以共享的)
局部變量,方法定義參數和異常處理器參數不會在線程間共享
(2)JMM結構模型
1. 線程A將主內存共享變量讀取讀入線程A的工作內存後並進行操作,之後將數據重 新寫回到主內存中
2. 線程B從主存中讀取最新的共享變量
如果線程A更新後沒有及時寫會主內存而此時線程B讀到的是過期的數據出現了“髒讀”現象
可以通過同步機制或者volatile關鍵字讓共享變量強制刷新到內存 變量具有可見性
(2)重排序
爲了提高性能,編譯器和處理器常常會對指令進行重排序
- 編譯器優化的重排序。
- 指令級並行的重排序
- 內存系統的重排序
as-if-serial不管怎麼重排序(編譯器和處理器爲了提供並行度)(單線程)程序的執行結果不能被改變。
- volatile保持內存可見性和防止指令重排序
(3)happens-before
JMM可以通過happens-before關係向程序員提供跨線程的內存可見性保證
如果A線程的寫操作a
B線程的讀操作b
存在happens-before關係,儘管a操作和b操作在不同的線程中執行,但JMM向程序員保證a操作將對b操作可見
- 如果一個操作happens-before另一個操作,那麼第一個操作的執行結果將對第二個操作可見,而且第一個操作的執行順序排在第二個操作之前。
- 兩個操作之間存在happens-before關係,並不意味着Java平臺的具體實現必須要按照happens-before關係指定的順序來執行。
as-if-serial |
happens-before |
單線程內程序的執行結果不被改變 |
多線程程序的執行結果不被改變。 |
儘可能單線程程序是按程序的順序來執行的 |
儘可能正確同步的多線程程序是按happens-before指定的順序來執行的。 |
- as-if-serial語義和happens-before這麼做的目的,都是爲了在不改變程序執行結果的前提下,儘可能地提高程序執行的並行度。
(4)happens-before具體規則
· 程序順序規則:一個線程中的每個操作,happens-before於該線程中的任意後續操作。
· 監視器鎖規則:對一個鎖的解鎖,happens-before於隨後對這個鎖的加鎖。
· volatile變量規則:對一個volatile域的寫,happens-before於任意後續對這個volatile域的讀。
· 傳遞性:如果A happens-before B,且B happens-before C,那麼A happens-before C。
· start()規則:如果線程A執行操作ThreadB.start()(啓動線程B),那麼A線程的ThreadB.start()操作happens-before於線程B中的任意操作。
· join()規則:如果線程A執行操作ThreadB.join()併成功返回,那麼線程B中的任意操作happens-before於線程A從ThreadB.join()操作成功返回。
· 程序中斷規則:對線程interrupted()方法的調用先行於被中斷線程的代碼檢測到中斷時間的發生。
· 對象finalize規則:一個對象的初始化完成(構造函數執行結束)先行於發生它的finalize()方法的開始。
- 總結
- 對於會改變程序執行結果的重排序,JMM要求編譯器和處理器必須禁止這種重排序。
- 對於不會改變程序執行結果的重排序,JMM對編譯器和處理器不做要求(JMM允許這種 重排序)