JMM(Java內存模型)是什么?為什么使用並發?


1、計算機

  首先我們需要講解下計算機的模型:現代計算機模型是基於-馮諾依曼計算機模型

  我們不用管輸入和輸出設備,最主要的就是中間計算器和存儲器之間的交互,也就是CPU與主內存之間取數、存數。

  大家會看到有一個IO總線在進行數據的流通,所以CPU與此磁盤的交互也會通過IO總線,但是IO總線上有其他的一些數據在流通,比如顯示器、鼠標鍵盤等,並且現在的CPU計算速度普遍在GHz,但是我們的內存輸出的卻是MB,這大大影響了CPU,因為每次取數都要經過IO中線,難免會影響IO,所以CPU有了三級緩存,L1、L2、L3,緩存存儲些指令集什么的。如下圖:

CPU讀取存儲器數據過程
  CPU要取寄存器XX的值,只需要一步:直接讀取。
  CPU要取L1 cache的某個值,需要1-3步(或者更多):把cache行鎖住,把某個數據拿來,解鎖,如果沒鎖住就慢了。
  CPU要取L2 cache的某個值,先要到L1 cache里取,L1當中不存在,在L2里,L2開始加鎖,加鎖以后,把L2里的數據復制到L1,再執行讀L1的過程,上面的3步,再解鎖。
  CPU取L3 cache的也是一樣,只不過先由L3復制到L2,從L2復制到L1,從L1到CPU。
  CPU取內存則最復雜:通知內存控制器占用總線帶寬,通知內存加鎖,發起內存讀請求,等待回應,回應數據保存到L3(如果沒有就到L2),再從L3/2到L1,再從L1到CPU之后解除總線鎖定。
  緩存一致性問題:當有多個處理器的時候,如何保證CPU1與CPU2之間的數據是一致的,為了保證數據性一致,需要在訪問緩存的同時,遵循一些協議如MESI,最后以誰的緩存為最后結果。

2、什么是線程

  現代操作系統在運行一個程序時,會為其創建一個進程。例如,啟動一個Java程序,操作系統就會創建一個Java進程。現代操作系統調度CPU的最小單元是線程,也叫輕量級進程(Light Weight Process),在一個進程里可以創建多個線程,這些線程都擁有各自的計數器、堆棧和局部變量等屬性,並且能夠訪問共享的內存變量。處理器在這些線程上高速切換,讓使用者感覺到這些線程在同時執行。線程的實現可以分為兩類:
  1、用戶級線程(User-Level Thread)
  2、內核線線程(Kernel-Level Thread)
  用戶線程:指不需要內核支持而在用戶程序中實現的線程,其不依賴於操作系統核心,應用進程利用線程庫提供創建、同步、調度和管理線程的函數來控制用戶線程。另外,用戶線程是由應用進程利用線程庫創建和管理,不依賴於操作系統核心。不需要用戶態/核心態切換,速度快。操作系統內核不知道多線程的存在,因此一個線程阻塞將使得整個進程(包括它的所有線程)阻塞。由於這里的處理器時間片分配是以進程為基本單位,所以每個線程執行的時間相對減少。
  內核線程:線程的所有管理操作都是由操作系統內核完成的。內核保存線程的狀態和上下文信息,當一個線程執行了引起阻塞的系統調用時,內核可以調度該進程的其他線程執行。在多處理器系統上,內核可以分派屬於同一進程的多個線程在多個處理器上運行,提高進程執行的並行度。由於需要內核完成線程的創建、調度和管理,所以和用戶級線程相比這些操作要慢得多,但是仍然比進程的創建和管理操作要快。大多數市場上的操作系統,如Windows,Linux等都支持內核級線程。

 

   我們每創建一個線程,jvm都會幫我們在內核空間創建一個線程,這樣就避免了jvm來管理線程使操作系統不知道線程的存在,只知道jvm這一個進程,所以一旦jvm中的線程堵塞,CPU是不會進行上下文切換的,導致整個jvm進程都會堵塞。

三、為什么用到並發?並發會產生什么問題?

  並發編程的本質其實就是利用多線程技術,在現代多核的CPU的背景下,催生了並發編程的趨勢,通過並發編程的形式可以將多核CPU的計算能力發揮到極致,性能得到提升。除此之外,面對復雜業務模型,並行程序會比串行程序更適應業務需求,而並發編程更能吻合這種業務拆分 。
  即使是單核處理器也支持多線程執行代碼,CPU通過給每個線程分配CPU時間片來實現這個機制。時間片是CPU分配給各個線程的時間,因為時間片非常短,所以CPU通過不停地切換線程執行,讓我們感覺多個線程是同時執行的,時間片一般是幾十毫秒(ms)。
  並發不等於並行:並發指的是多個任務交替進行,而並行則是指真正意義上的“同時進行”。實際上,如果系統內只有一個CPU,而使用多線程時,那么真實系統環境下不能並行,只能通過切換時間片的方式交替進行,而成為並發執行任務。真正的並行也只能出現在擁有多個CPU的系統中。
  並發的優點:
    1充分利用多核CPU的計算能力;
    2方便進行業務拆分,提升應用性能;
   並發產生的問題:
    1.高並發場景下,導致頻繁的上下文切換
       2.臨界區線程安全問題,容易出現死鎖的,產生死鎖就會造成系統功能不可用
  CPU通過分配時間片來每次循環任務,每次需要切換到下一個任務時,都會保存上一個任務的結束狀態,以便切換回來可以在加載這個任務,所以任務從保存到再加載的過程就是一次上下文切換。

四、什么是JMM模型?

  JMM並不真實存在,只是一種規范,通過這種規范來讓定義程序中各個變量的訪問方式。JVM運行程序的實體是線程,而每個線程創建時JVM都會為其創建一個工作內存(有些地方稱為棧空間),用於存儲線程私有的數據,而Java內存模型中規定所有變量都存儲在主內存,主內存是共享內存區域,所有線程都可以訪問,但線程對變量的操作(讀取賦值等)必須在工作內存中進行,首先要將變量從主內存拷貝的自己的工作內存空間,然后對變量進行操作,操作完成后再將變量寫回主內存,不能直接操作主內存中的變量,工作內存中存儲着主內存中的變量副本拷貝,前面說過,工作內存是每個線程的私有數據區域,因此不同的線程間無法訪問對方的工作內存,線程間的通信(傳值)必須通過主內存來完成

  JMM是圍繞原子性,有序性、可見性展開
JMM-同步八種操作介紹
  (1)lock(鎖定):作用於主內存的變量,把一個變量標記為一條線程獨占狀態
  (2)unlock(解鎖):作用於主內存的變量,把一個處於鎖定狀態的變量釋放出來,釋放后的變量才可以被其他線程鎖定
  (3)read(讀取):作用於主內存的變量,把一個變量值從主內存傳輸到線程的工作內存中,以便隨后的load動作使用
  (4)load(載入):作用於工作內存的變量,它把read操作從主內存中得到的變量值放入工作內存的變量副本中
  (5)use(使用):作用於工作內存的變量,把工作內存中的一個變量值傳遞給執行引擎
  (6)assign(賦值):作用於工作內存的變量,它把一個從執行引擎接收到的值賦給工作內存的變量
  (7)store(存儲):作用於工作內存的變量,把工作內存中的一個變量的值傳送到主內存中,以便隨后的write的操作
  (8)write(寫入):作用於工作內存的變量,它把store操作從工作內存中的一個變量的值傳送到主內存的變量中
  如果要把一個變量從主內存中復制到工作內存中,就需要按順序地執行read和load操作,如果把變量從工作內存中同步到主內存中,就需要按順序地執行store和write操作。但Java內存模型只要求上述操作必須按順序執行,而沒有保證必須是連續執行。

 

ps:關注一下本人博客,每周都有更新內容哦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM