併發編程中,須要處理兩個問題:java
定義:是指線程之間以何種機制來交換信息。
在命令式編程中,線程通訊機制有兩種:共享內存和消息傳遞。程序員
在共享內存的併發模型裏,線程之間共享程序的公共狀態,線程之間經過寫-讀內存中的公共狀態來隱式的通訊。
在消息傳遞的併發模型裏,線程之間沒有公共狀態,線程之間必須經過明確的發送消息來顯示的進行通訊。編程
定義:是指程序用於控制不一樣線程之間操做發生相對順序的機制。
在共享內存併發模型裏,同步是顯示進行的。程序必須指定某個方法或某個代碼段須要在線程之間互斥執行。
在消息傳遞併發模型裏,因爲消息的發送必須在消息的接收以前,所以同步是隱式進行的。數組
java採用的是共享內存模型,java線程之間的通訊是隱式進行,整個通訊過程對程序透明。緩存
在java中,全部實例域,靜態域和數組元素都存儲在堆內存中,堆內存在線程之間共享(共享變量)
局部變量(Local variables),方法定義參數(java語言規範稱之爲formal method parameters)和異常處理器參數(exception handler parameters)不會在線程之間共享,它們不會有內存可見性問題,也不受內存模型的影響。併發
Java線程之間的通訊由Java內存模型(本文簡稱爲JMM)控制,JMM決定一個線程對共享變量的寫入什麼時候對另外一個線程可見。app
從抽象的角度來看,JMM定義了線程和主內存之間的抽象關係:線程之間的共享變量存儲在主內存(main memory)中,每一個線程都有一個私有的本地內存(local memory),本地內存中存儲了該線程以讀/寫共享變量的副本。性能
本地內存是JMM的一個抽象概念,並不真實存在。它涵蓋了緩存,寫緩衝區,寄存器以及其餘的硬件和編譯器優化。優化
爲了提升性能,編譯器和處理器經常會對指令作重排序。重排序分爲三種類型:線程
處理器對內存的讀/寫操做的執行順序,不必定與內存實際發生的讀/寫操做順序一致!
這裏處理器A和處理器B能夠同時把共享變量寫入本身的寫緩衝區(A1,B1),而後從內存中讀取另外一個共享變量(A2,B2),最後才把本身寫緩存區中保存的髒數據刷新到內存中(A3,B3)。當以這種時序執行時,程序就能夠獲得x = y = 0的結果。
從內存操做實際發生的順序來看,直處處理器A執行A3來刷新本身的寫緩存區,寫操做A1纔算真正執行了。雖然處理器A執行內存操做的順序爲:A1->A2,但內存操做實際發生的順序倒是:A2->A1。此時,處理器A的內存操做順序被重排序了。
若是一個操做執行的結果須要對另外一個操做可見,那麼這兩個操做之間必需要存在happens-before關係。這裏提到的兩個操做既能夠是在一個線程以內,也能夠是在不一樣線程之間。
與程序員密切相關的happens-before規則以下: