併發編程之Java內存模型

  在介紹Java內存模型以前,先來了解一下爲何要有內存模型,以及內存模型是什麼。而後咱們基於對內存模型的瞭解,學習Java內存模型以及併發編程的三大特性。java

 

爲何要有內存模型程序員

  在計算機中,全部的運算操做都是由CPU的寄存器來完成的,CPU指令的執行須要涉及到數據的讀寫操做,而CPU只能訪問主存中的數據。隨着技術的發展,CPU的執行速度愈來愈快,而內存的訪問速度沒有太大的變化,致使CU每次操做主存都要等待很長的時間。因而就有了在CPU與主存之間添加緩存的設計。編程

 

內存模型:CPU Cache模型緩存

 

  目前緩存的數量達到了3級,最接近CPU的緩存稱爲L1,而後爲L2、L3和主存。因爲程序指令和數據的行爲和熱點分佈差別比較大,所以將L1又細分爲L1i(istruction)、L1d(data)。多線程

   CPU的出現是爲了解決CPU直接訪問主存效率低下的問題。程序在運行過程當中,會將運算所需的數據從主存中複製一份到Cache中,這樣CPU在計算時就能夠直接對CPU Cache中的數據進行讀寫,當運算結束後,再將Cahce中的最新數據刷新到主存中。
 
  雖然緩存的出現極大地提高了CPU的吞吐能力,可是也致使了緩存不一致的問題。這是由於CPU都是對Cache中的數據進行讀寫,不一樣線程之間的工做內存是相互獨立的,對某個線程工做空間中的數據進行更新,可能會沒法及時同步到其它緩存中。
 
  爲了保證數據的正確性,內存模型定義了共享內存系統中多線程程序讀寫操做行爲的規範。

 

Java內存模型架構

    Java內存模型(Java Memory Model ),簡稱JMM,是一種符合內存模型規範的,屏蔽了各類硬件和操做系統的訪問差別的,保證了Java程序在各類平臺下對內存的訪問都能獲得一致效果的機制及規範。其目的是解決多線程經過主內存進行通訊時,存在的原子性、可見性(緩存一致性)以及有序性問題。(關於原子性、可見性(緩存一致性)以及有序性,咱們將會在」併發編程的三大特性「中詳細講解)
 
    JMM決定了一個線程對共享變量的寫入什麼時候對其它線程可見,定義了線程與主存之間的關係:
  • 共享變量存儲於主存中,每一個線程均可以訪問
  • 每一個線程都有私有的工做內存,也稱爲本地內存
  • 工做內存中只存儲共享變量的副本
  • 線程不能直接操做主存,只有操做了本地內存後才能寫入主存
  • 每個線程都不能訪問其餘線程的本地內存
 

 併發編程的三大特性併發

    併發編程有三大特性:原子性、可見性、有序性。
    •   原子性:是指在一次操做或屢次操做中,要麼全部的操做都獲得執行,要麼都不執行。【相似於事務】
      •   JMM只保證了基本讀取和賦值的原子性操做
      •   多個原子性操做的組合再也不是原子性操做
      •   可使用synchronized/lock保證某些代碼片斷的原子性
      •   對於int等類型的自增操做,能夠經過java.util.concurrent.atomic.*保證原子性
    •   可見性:是指一個線程對共享變量進行了修改,其餘線程能夠當即看到修改後的值。
    •   有序性:是指代碼在執行過程當中的前後順序是有序的。【Java編譯器會對代碼進行優化,執行順序可能與開發者編寫的順序不一樣(指令重排)】
 
  併發編程時,保證三大特性的方式有三種:
    •   使用volatile關鍵字修飾變量
      •   當一個變量被volatile關鍵字修飾時,對於共享變量的讀操做會直接在主存中進行,對於共享變量的寫操做是先修改本地內存,修改結束後直接刷到主存中。(未被volatile修飾的變量被修改後,何時最新值會被刷到主存中是不肯定的)
    •   使用synchronized關鍵字修飾方法或代碼塊
      •   synchronized關鍵字能保證同一時刻只有一個線程得到鎖而後執行同步方法,而且確保鎖釋放以前,會將修改的變量刷入主存。
    •   使用JUC提供的顯式鎖Lock
      •   Lock能保證同一時刻只有一個線程得到鎖而後執行同步方法,而且確保鎖釋放以前,會將修改的變量刷入主存。

 補充高併發

  Java中提供了一系列和併發處理相關的關鍵字,好比volatile、synchronized等,其實這些就是Java內存模型封裝了底層的實現後提供給程序員使用的一些關鍵字。學習

 

參考文獻優化

  • 汪文君《Java高併發編程詳解-多線程與架構設計》
相關文章
相關標籤/搜索