Java內存模型

Java內存模型(JMM)app

  Java內存模型規定了全部的變量都存儲在主內存中。每條線程都有本身的工做內存,線程對變量的全部操做都必須在工做內存中進行,而不能直接讀寫主內存中的變量。不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量值的傳遞均需經過主內存來完成。this

內存間交互操做:Java內存模型定義了8種操做來完成。spa

  lock(鎖定):做用於主內存的變量,他把一個變量標識爲一條線程獨佔的狀態。
線程

  unlock(解鎖):做用於主內存的變量,它把一個處於鎖定狀態的變量釋放出來,釋放後的變量才能夠被其餘線程鎖定。對象

  read(讀取):做用於主內存的變量,它把一個變量的值從主內存傳輸到線程的工做內存中,一邊隨後的load動做使用。排序

  load(載入):做用於工做內存的變量,它把read操做從主內存中獲得的變量值放入工做內存的變量副本中。事件

  use(使用):做用於工做內存的變量,它把工做內存中一個變量的值傳遞給執行引擎,每當虛擬機遇到一個須要使用到變量的值的字節碼指令時將會執行這個操做。內存

  assign(賦值):做用於工做內存的變量,它把一個從執行引擎接收到的值賦給工做內存的變量,每當虛擬機遇到一個給變量賦值的字節碼指令時執行這個操做。開發

  store(存儲):做用於工做內存的變量,它把工做內存中一個變量的值傳送到主內存中,以便隨後的write操做使用。同步

  write(寫入):做用於主內存的變量,他把store操做從工做內存中獲得的變量的值放入主內存的變量中。

  若是要把一個變量從主內存複製到工做內存,就要順序地執行read和load操做;若是要把一個變量從工做內存同步回主內存,就要順序地執行store和write操做;

Java內存模型規定了在執行上述8種操做時必須知足以下規則:

  1)不容許read和load、store和write操做之一單獨出現;

  2)不容許一個線程丟棄它最近的assign操做,即變量在工做內存中改變了以後必須把該變化同步回主內存;

  3)不容許一個線程無緣由地把數據從線程的工做內存同步回主內存;

  4)一個新的變量只能在主內存中誕生,不容許在工做內存中直接使用一個未被初始化的變量;

  5)一個變量在同一時刻只容許一條線程對其進行lock操做,但lock操做能夠被同一條線程重複執行屢次。屢次執行lock後,只有執行相同次數的unlock操做,變量纔會被解鎖;

  6)若是對一個變量執行lock操做,那將會清空工做內存中此變量的值,在執行引擎使用這個變量前,須要從新執行load或assign操做初始化變量的值;

  7)若是一個變量事先沒有被lock操做鎖定,就不容許對它執行unlock操做,也不容許去unlock一個被其餘線程鎖定的變量;

  8)對一個變量執行unlock操做以前,必須先把此變量同步回主內存中;

Volatile關鍵字

  是Java虛擬機提供的最輕量級的同步機制,用來確保將變量的更新操做通知到其餘線程。volatile變量具有兩種特性,其一是保證該變量對全部線程的可見性,可見性是指當一個線程修改了變量的值,那麼新值對與其餘線程是能夠當即獲取的。可見性的保正是基於CPU的內存屏障指令,即happens-before原則。對於一個volatile變量的寫操做先行發生於對其的讀操做。其二是禁止指令重排序,指令重排序是指CPU採用了容許將多條指令不按程序規定的順序分開發送給各個相應的電路單元處理。

  在訪問volatile變量時,不會執行加鎖操做,因此不會形成線程阻塞,所以volatile變量是一種比synchronized關鍵字更輕量級的同步機制。volatile適合場景:一個變量被多個線程共享,線程直接給這個變量賦值。

原子性、可見性與有序性

  原子性:即一個或多個操做,要麼所有執行且執行過程不被任何因素打斷,要麼所有不執行。read、load、assign、use、store和write這些基本數據類型的訪問讀寫是具有原子性的(除了long和double的非原子性協定)。若須要更大範圍的原子性保證,能夠經過synchronized和lock實現。對於long和double型變量,容許虛擬機實現選擇能夠不保證64位數據類型的load、store、read和write這4個操做的原子性,即long和double的非原子性協定。

  可見性:指當一個線程修改了共享變量的值,其餘線程可以當即得知這個值。Java內存模型是經過在變量修改後將新值同步回主內存,在變量讀取錢從主內存刷新變量值這種依賴主內存做爲傳遞媒介的方式實現可見性的。除了volatile以外,Java還有synchronized、final及lock實現了可見性。同步塊的可見性是由對一條變量執行unlock操做以前,必須把此變量同步回主內存中這條規則得到的;final關鍵字的可見性是指被final修飾的字段在構造器中一旦初始化完成,而且構造器沒有把this的引用傳遞出去,那在其餘線程中就能看到final字段的值。

  有序性:即程序執行的順序按照代碼的前後順序執行。Java語言提供了volatile和synchronized關鍵字來保證線程之間操做的有序性。volatile關鍵字自己就包含了禁止指令重排序的語義,而synchronized是由一個變量在同一時刻只容許一條線程對其進行lock操做這條規則得到的,這條規則決定了持有同一個鎖的兩個同步塊只能串行地執行。

先行發生原則(happens-before原則):先行發生是Java內存模型定義的亮相操做之間的偏序關係。

  程序次序規則:在一個線程內,按照程序代碼順序,書寫在前面的操做先行發生於書寫在後面的操做;

  管程鎖定規則:一個unlock操做先行發生於後面對同一個鎖的lock操做;

  volatile變量規則:對一個volatile變量的寫操做先行發生於後面對這個變量的讀操做;

  線程啓動規則:Thread對象的start()方法先行發生於此線程的每個動做;

  線程終止規則:線程中全部操做先行發生於對此線程的終止檢測。能夠經過Thread.jion()方法結束、Thread.isAlive()的返回值等手段檢測到線程已經終止執行;

  線程中斷規則:對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到的中斷事件的發生;

  對象終結規則:一個對象的初始化完成先行發生於它的finalize()方法的開始;

  傳遞性:若是操做A先行發生於操做B,操做B先行發生於操做C,那就能夠得出操做A先行發生於操做C的結論。

相關文章
相關標籤/搜索