Java內存模型&volatile關鍵字

java內存模型(JMM):java

相關概念:
	1)在命令式編程中,線程之間的通訊機制有兩種:共享內存和消息傳遞。
	2)java的併發採用的是共享內存模型:經過讀/寫內存中的公共狀態進行隱式通訊。

概念:java線程之間的通訊是由java內存模型控制的,JMM決定一個線程對共享變量的寫入什麼時候對另外一個線程可見。

說明:
	1>線程之間的共享變量存儲在主內存中,每一個線程都有一個私有的工做內存,工做內存中存儲了該線程讀/寫共享變量的副本。
	2>工做內存是JMM的一個抽象概念,並不真實存在。它涵蓋了緩存、寫緩衝區、寄存器以及其餘的硬件和編譯器優化。
	3>線程對變量的全部操做(讀取、賦值等)都必須在工做內存中進行,而不能直接讀寫主內存中的變量。
	4>不一樣的線程之間也沒法直接訪問對方工做內存中的變量,線程間變量值的傳遞均須要經過主內存來完成。

volatile關鍵字:編程

相關概念:
	緩存行:緩存器中能夠分配的最小存儲單位。
	L1緩存:內部緩存。
	L2緩存:外部緩存。

原理:
	1)爲了提升處理速度,處理器不直接和內存進行通訊,而是先將系統內存中的數據讀到緩存(L一、L2)後再進行操做,但操做完成後,處理器是不知道什麼時候要把操做後的數據寫回到內存中。
	2)對volatile修飾的變量進行寫操做時,JVM會向處理器發送一條Lock前綴的指令,將這個變量所在緩存行(即JMM中的工做內存)的數據寫回到系統內存中,而且將其它CPU裏緩存了該內存地址的數據無效。
	補充:
		1>對volatile修飾的變量進行寫操做(賦值)時,在JIT編譯器生成的彙編指令中,咱們會發現有一個以Lock爲前綴的指令。
		2>以Lock爲前綴的指令在多核處理器下會引起了兩件事情:①將當前處理器緩存行的數據寫回到系統內存中 ②這個寫回內存的操做會致使其它CPU裏緩存了該內存地址的數據無效。
	
	volatile的內存原語:
		當讀一個volatile變量時,JMM會把該線程對應的工做內存置爲無效,線程接下來將從主內存中讀取共享變量。
		當寫一個volatile變量時,JMM會把該線程對應的工做內存中的共享變量值刷新到主內存。
	即:
		1將本地內存中的數據設置爲無效,  
		2從主內存中將數據複製到本地內存中,  
		3在本地內存中進行操做,  
		4操做完成後將本地內存中的數據刷新到主內存中。總體看起來就像是直接在主內存中操做同樣。  
		
	
說明:
	用volatile修飾的變量若是被一個線程更改了,那麼其它的線程都會當即感知,而且每一個線程獲取該變量的值都是最新的值,訪問volatile修飾的變量看起來就像是直接在內存中讀寫同樣。
	
特性:
	可見性:對一個volatile變量的讀,(任意線程)老是能看到對這個volatile變量最後的寫入。
	原子性:對一個volatile變量的讀/寫具備原子性,但相似於volatile++這種複合操做不具備原子性。			

優勢:
	不會引發線程上下文的切換

	
volatile與synchronized的比較:
	1)關鍵字volatile只能修飾變量,synchronized能夠修飾代碼塊、方法
	2)volatile不能保證原子性,synchronized保證原子性:
		volatile能夠保證數據的可見性,可是不能保證原子性,因此volatile解決的是變量在多線程之間的可見性;
		synchronized能夠保證原子性,也保證了可見性(synchronized會將私有內存和公共內存中的數據作同步),因此synchronized解決的是多線程之間訪問資源的同步性。

重排序:緩存

說明:在執行程序時,爲了提升性能,編譯器和處理器經常會對指令作重排序。

重排序分2種類型:

	1)編譯器重排序:編譯器在不改變單線程程序語義的前提下,能夠從新安排語句的執行順序。
	2)處理器重排序:
		1>指令級並行的重排序:現代處理器採用了指令級並行技術來將多條指令重疊執行。若是不存在數據依賴性,處理器能夠改變語句對應機器指令的執行順序。
		2>內存系統的重排序:  因爲處理器使用緩存和讀/寫緩衝區,這使得加載和存儲操做看上去多是在亂序執行。


JMM如何實現volatile寫/讀的內存語義:

	1)JMM針對編譯器制定的volatile重排序規則:
	
		兩個操做間重排序的條件:
			1>當第一個操做是volatile讀,無論第二個操做是什麼,都不能重排序。這個規則確保volatile讀以後的操做不會被編譯器重排序到volatile讀以前。
			2>當第二個操做是volatile寫,無論第一個操做是什麼,都不能重排序。這個規則確保volatile寫以前的操做不會被編譯器重排序到volatile寫以後。
			3>當第一個操做是volatile寫,第二個操做是volatile讀時,不能重排序。
		
			由以上3點能夠得出結論:兩個volatile變量操做不可以進行重排序。
		
	2)爲了實現volatile的內存語義,編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。(內存屏障:將前面操做的共享變量值刷新到主內存中。)
	
		
		
	
參考資料:java併發編程的藝術
相關文章
相關標籤/搜索