共享內存
程之間共享程序的公共狀態,經過寫-讀內存中的公共狀態進行隱式通訊
程序員
消息傳遞
程之間沒有公共狀態,線程之間必須經過發送消息來顯式進行通訊
編程
線程之間沒有公共狀態,線程之間必須經過發送消息來顯式進行通訊
數組
總結:Java的併發採用的是共享內存模型,Java線程之間的通訊老是隱式進行,整個通訊過程對程序員徹底透明。緩存
附圖:
多線程
注:全部實例域、靜態域和數組元素都存儲在堆內存中,堆內存在線程之間共享(本章用「共享變量」這個術語代指實例域,靜態域和數組元素)。局部變量(Local Variables),方法定義參數(Java語法規範稱之爲Formal Method Parameters)和異常處理器參數(Exception HandlerParameters)不會在線程之間共享,它們不會有內存可⻅性問題,也不受內存模型的影響。併發
重排序是指編譯器和處理器爲了優化程序性能而對指令序列進行從新排序的一種手段。
現代處理器採用了指令級並行技術(Instruction-Level Parallelism,ILP)來將多條指令重疊執行。若是不存在數據依賴性,處理器能夠改變語句對應機器指令的執行順序
內存系統的重排序
因爲處理器使用緩存和讀/寫緩衝區,這使得加載和存儲操做看上去多是在亂序執行app
B能夠排在A前
,JMM容許這種排序。public class DoubleCheckedLocking { // 1 private static Instance instance; // 2 public static Instance getInstance() { // 3 if (instance == null) { // 4:第一次檢查 synchronized (DoubleCheckedLocking.class) { // 5:加鎖 if (instance == null) // 6:第二次檢查 instance = new Instance(); // 7:問題的根源出在這裏 } // 8 } // 9 return instance; // 10 } // 11 }
memory = allocate(); //1:分配對象的內存空間 ctorInstance(memory); //2:初始化對象 instance = memory; //3:設置instance指向剛分配的內存地址
memory = allocate(); //1:分配對象的內存空間 instance = memory; //3:設置instance指向剛分配的內存地址 //注意,此時對象尚未被初始化! ctorInstance(memory); //2:初始化對象
解決方案性能
volatile
]);第一種基於volatile
方案優化
public class SafeDoubleCheckedLocking { private volatile static Instance instance; public static Instance getInstance() { if (instance == null) { synchronized (SafeDoubleCheckedLocking.class) { if (instance == null) instance = new Instance(); // instance爲volatile,如今沒問題了 } } return instance; } }
public class InstanceFactory { private static class InstanceHolder { public static Instance instance = new Instance(); } public static Instance getInstance() { return InstanceHolder.instance; // 這裏將致使InstanceHolder類被初始化,存在初始化鎖,拿不到的線程會一直等待 } }
happens-before是JMM最核心的概念
線程
JMM對程序員的承諾
]JMM對編譯器和處理器重排序的約束原則
]注意:兩個操做之間具備happens-before關係,並不意味着前
一個操做必需要在後一個操做以前執行!happens-before僅僅要求前一個操
做(執行的結果)對後一個操做可見,且前一個操做按順序排在第一個操
做以前(the first is visible to and ordered before the second)。
話外語:
- as-if-serial語義保證單線程內程序的執行結果不被改變,happens-before關係保證正確同步的多線程程序的執行結果不被改變
- as-if-serial語義和happens-before這麼作的目的,都是爲了在不改變程序執行結果的前提下,儘量地提升程序執行的並行度