1. 程式人生 > >深入理解Java虛擬機器(七)之Java記憶體模型

深入理解Java虛擬機器(七)之Java記憶體模型

深入理解Java虛擬機器系列文章

Java記憶體模型規定了所有的變數都儲存在主記憶體,每個執行緒都有自己的工作記憶體,執行緒中的工作記憶體儲存了被該執行緒使用到的變數的主記憶體的副本拷貝。執行緒對變數的所有操作都在工作記憶體中進行,不同的執行緒之間變數值的傳遞需要通過主記憶體來完成

volatile

可見性:當一個執行緒修改了變數的值,新值對於其他執行緒來說是立即得知的

  • 一個volatile變數具有2種特性:保證對所有執行緒的可見性;禁止指令重排序優化

需要注意的是volatile只保證可見性,不保證原子性。在不符合以下2條規則的運算場景中,volatile變數的運算在併發下一樣是不安全的。

  • 運算結果並不依賴變數當前額值,或者能夠確保只有單一的執行緒修改變數的值
  • 變數不需要與其它的狀態量共同參與不變約束
  • volatile的禁止重排序就像一個記憶體屏障一樣,可以保證被修飾的變數的操作在程式中的順序,從而達到其他指令重排序也不會越過這個記憶體屏障的效果
  • volatile的可見性是在執行volatile變數的賦值操作時,都會執行儲存、寫入主記憶體的操作,從而達到主記憶體中的變數始終是最新的效果;而在執行載入操作時,都會先執行從主記憶體讀的操作,從而保證每次載入的變數都是主記憶體中最新的。以上就達到了可見性
  • 原子性:基本資料型別的訪問讀寫都是具備原子性的,synchronized塊之間的操作也具備原子性
  • 可見性:Java記憶體模型是通過在變數修改後將新的值同步回主記憶體,在變數讀取前從主記憶體重新整理變數值這種依賴主記憶體作為傳遞媒介的方式實現可見性。除了volatile關鍵字,還有synchronized和final能夠實現可見性 有序性:Java中volatile和synchronized關鍵字可以保證執行緒之間操作的有序性
先行發生原則

先行發生原則:是Java記憶體模型中定義的2項操作之間的偏序關係,如果說操作A先行發生於操作B,其實就是說在發生操作B之前,操作A產生的影響能被操作B觀察到

Java記憶體模型下的一些天然的先行發生關係:

  • 程式次序規則:在一個執行緒內,按照程式的控制流順序,前面的操作先行發生於後面的操作
  • 管程鎖定規則:一個unlock操作先行發生於後面(時間上)對同一個鎖的lock操作
  • volatile變數規則:對一個volalite變數的寫操作先行發生於後面對這個變數的讀操作
  • 執行緒啟動規則:Thread物件的start方法先行發生於此執行緒的每一個動作
  • 執行緒終止規則:執行緒中的所有操作都先行發生於對次執行緒的終止檢測
  • 執行緒中斷規則:對執行緒interrupt方法的呼叫先行發生於被中斷執行緒的程式碼檢測到中斷事件的發生
  • 物件終結規則:一個物件的初始化完成(建構函式執行結束)先行發生於它的finalize方法的開始
  • 傳遞性:如果操作A先行發生於操作B,操作B先行發生於操作C,那就可以得出操作A先行發生於操作C的結論
Java執行緒
  • Java使用的執行緒排程方式是搶佔式排程
  • Java定義的執行緒狀態:新建、執行、無限期等待、限期等待和阻塞,當然還有結束
Java中的執行緒安全

Java語言中執行緒安全程度由強到弱可分為:不可變、絕對執行緒安全、相對執行緒安全、執行緒相容和執行緒對立

  • 不可變:如果共享的資料是一個基本資料型別,那麼只要在定義時使用final關鍵字修飾就可以保證是不可變。不可變型別有String、列舉型別、Long和Double的包裝類、BigInteger和BigDecimal等
  • Java API中的執行緒安全類大部分都是相對執行緒安全的類,需要保證對這個物件單獨的操作是執行緒安全的,有可能還需要在呼叫端使用額外的同步手段來保證呼叫的正確性。如Vector、HashTable等
  • 執行緒相容是指本身並不是執行緒安全的,但是可以通過呼叫端正確地使用同步手段來保證物件在併發環境中的安全使用,如ArrayList、HashMap等
執行緒安全的實現方法

互斥同步:指在多個執行緒併發訪問共享資料時,保證共享資料在同一個時刻只被一個(或是一些,使用訊號量的時候)執行緒使用。

  • 最基本的互斥同步手段就是synchronized關鍵字。如果synchronized明確指定了物件引數,就是鎖定這個物件;如果沒有明確指定,那鎖定就是物件例項或Class物件
  • 此外還可以使用java.util.concurrent包中的重入鎖(ReentrantLock)來實現同步,ReentrantLock相比synchronized增加了一些功能,主要是:等待可中斷、可實現公平鎖、鎖可以繫結多個條件等

可重入程式碼:可以在程式碼執行的任何時刻中斷,轉而去執行另外一段程式碼,在控制權返回後原來的程式不會出現任何錯誤。所有的可重入的程式碼都是執行緒安全的。

  • 如果一個方法,它的返回結果是可以預測的,只要輸入了相同的資料,就都能返回相同的結果,那它就滿足可重入性的要求,就是執行緒安全的

歡迎關注我的微信公眾號,和我一起學習一起成長! AntDream