通過例項解析JMM和Volatile底層原理
這篇文章主要介紹了通過例項解析JMM和Volatile底層原理,文中通過示例程式碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
JMM和volatile分析
1.JMM:Java Memory Model,java執行緒記憶體模型
JMM:它是一個抽象的概念,描述的是執行緒和記憶體間的通訊,java執行緒記憶體模型和CPU快取模型類似,它是標準化的,用於遮蔽硬體和作業系統對記憶體訪問的差異性。
2.JMM和8大原子操作結合
3.volatile的應用及底層原理探究
volatile : 輕量級的synchronized,在多處理器的開發中保證了共享變數的"可見性"。可見性的意思:當一個執行緒修改了某個共享變數時,其他使用到該共享變數的執行緒能夠及時讀取到修改的值。修飾得當,比synchronized的執行成本更低,因為它不會引起執行緒上下文切換和排程。
public class VolatileTest { private static volatile boolean flag = false; public static void main(String[] args) { update(); } public static void update(){ flag = true; System.out.println(flag); } } Volatile JIT編譯器編譯java程式碼為彙編指令檢視 1.在jdk\jre\bin\ 目錄下新增 hsdis-amd64.lib 2.在jdk1.8\jre\bin\server\目錄下新增hsdis-amd64.dll檔案 3.在IDEA中設定 JVM引數 -server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,VolatileTest.update 4.執行Java程式即可打印出 CompilerOracle: compileonly *VolatileTest.update Loaded disassembler from E:\EclipseDev\jdk\jdk1.8\jre\bin\server\hsdis-amd64.dll Decoding compiled method 0x000000000f11aad0: Code: Argument 0 is unknown.RIP: 0xf11ac40 Code size: 0x000002a8 [Disassembling for mach='amd64'] [Entry Point] [Verified Entry Point] [Constants] # {method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest' # [sp+0x40] (sp of caller) 0x000000000f11ac40: mov dword ptr [rsp+0ffffffffffffa000h],eax 0x000000000f11ac47: push rbp 0x000000000f11ac48: sub rsp,30h 0x000000000f11ac4c: mov r8,8792d70h ; {metadata(method data for {method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest')} 0x000000000f11ac56: mov edx,dword ptr [r8+0dch] 0x000000000f11ac5d: add edx,8h 0x000000000f11ac60: mov dword ptr [r8+0dch],edx 0x000000000f11ac67: mov r8,8792b70h ; {metadata({method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest')} 0x000000000f11ac71: and edx,0h 0x000000000f11ac74: cmp edx,0h 0x000000000f11ac77: je 0f11ad68h ;*iconst_1 ; - com.yew.test.VolatileTest::update@0 (line 17) 0x000000000f11ac7d: mov r8,0d7b08a30h ; {oop(a 'java/lang/Class' = 'com/yew/test/VolatileTest')} 0x000000000f11ac87: mov edx,1h 0x000000000f11ac8c: mov byte ptr [r8+68h],dl volatile修飾 0x000000000f11ac90: lock add dword ptr [rsp],0h ;*putstatic flag ; - com.yew.test.VolatileTest::update@1 (line 17) 無Volatile修飾 0x000000000f113707: mov byte ptr [r8+68h],1h ;*putstatic flag ; - com.yew.test.VolatileTest::update@1 (line 17) 通過比較可知:改變共享變數flag的值為true,該變數由Volatile修飾,進行彙編列印時,會有lock字首修飾,根據IA-32架構軟體開發者手冊可知,lock字首指令在多核CPU處理器下會引發兩件事情: 【1】將當前處理器快取行的資料立即寫回系統記憶體 【2】wirte操作會使其他處理器中快取該記憶體地址的資料無效 LOCK#聲言期間,處理器獨佔任何共享記憶體。IA-32處理器和Intel 64處理器使用MESI(修改、獨佔、共享、無效)控制協議去維護內部快取和其他處理器快取的一致性。通過嗅探技術保證處理器內部快取、系統快取和其他處理器快取的資料再總線上保持一致。當其他處理器打算回寫記憶體地址,該地址是共享記憶體區域,那麼嗅探的處理器會將它的快取行設定為無效,下次訪問相同記憶體時,強制執行快取行填充。 0x000000000f11ac95: nop 0x000000000f11ac98: jmp 0f11add4h ; {no_reloc} 0x000000000f11ac9d: add byte ptr [rax],al 0x000000000f11ac9f: add byte ptr [rax],al 0x000000000f11aca1: add byte ptr [rsi+0fh],ah 0x000000000f11aca4: Fatal error: Disassembling failed with error code: 15Decoding compiled method 0x000000000f11ef50: Code: Argument 0 is unknown.RIP: 0xf11f080 Code size: 0x00000058 [Entry Point] [Verified Entry Point] [Constants] # {method} {0x0000000008792b78} 'update' '()V' in 'com/yew/test/VolatileTest' # [sp+0x20] (sp of caller) 0x000000000f11f080: mov dword ptr [rsp+0ffffffffffffa000h],eax 0x000000000f11f087: push rbp 0x000000000f11f088: sub rsp,10h 0x000000000f11f08c: mov r10,0d7b08a30h ; {oop(a 'java/lang/Class' = 'com/yew/test/VolatileTest')} 0x000000000f11f096: mov byte ptr [r10+68h],1h 0x000000000f11f09b: lock add dword ptr [rsp],0h ;*putstatic flag ; - com.yew.test.VolatileTest::update@1 (line 17) 0x000000000f11f0a0: mov edx,1ch 0x000000000f11f0a5: nop 0x000000000f11f0a7: call 0f0557a0h ; OopMap{off=44} ;*getstatic out ; - com.yew.test.VolatileTest::update@4 (line 18) ; {runtime_call} 0x000000000f11f0ac: int3 ;*getstatic out ; - com.yew.test.VolatileTest::update@4 (line 18) 0x000000000f11f0ad: hlt 0x000000000f11f0ae: hlt 0x000000000f11f0af: hlt 0x000000000f11f0b0: hlt 0x000000000f11f0b1: hlt 0x000000000f11f0b2: hlt 0x000000000f11f0b3: hlt 0x000000000f11f0b4: hlt 0x000000000f11f0b5: hlt 0x000000000f11f0b6: hlt 0x000000000f11f0b7: hlt 0x000000000f11f0b8: hlt 0x000000000f11f0b9: hlt 0x000000000f11f0ba: hlt 0x000000000f11f0bb: hlt 0x000000000f11f0bc: hlt 0x000000000f11f0bd: hlt 0x000000000f11f0be: hlt 0x000000000f11f0bf: hlt [Exception Handler] [Stub Code] 0x000000000f11f0c0: jmp 0f0883a0h ; {no_reloc} [Deopt Handler Code] 0x000000000f11f0c5: call 0f11f0cah 0x000000000f11f0ca: sub qword ptr [rsp],5h 0x000000000f11f0cf: jmp 0f057600h ; {runtime_call} 0x000000000f11f0d4: hlt 0x000000000f11f0d5: hlt 0x000000000f11f0d6: hlt 0x000000000f11f0d7: hlt true
4.volatile的使用優化
java併發大師Doug Li在jdk7併發包中新增了一個佇列集合LinkeTransferQueue,它在使用Volatile關鍵字修飾變數時,採用追加位元組的方式將變數填充到64位元組
volatile修飾變數在進行修改時,會進行LOCK前置指令加鎖,鎖住快取行的資料獨佔
適用於:快取行位元組為64位元組 處理器如 I7 酷睿 Pentium M等
不適用:非64位元組寬的快取行 P6系列或者奔騰 共享變數不會被頻繁的寫
5.併發程式設計的三大特性:可見性、原子性、有序性
volatile可以保證可見性、有序性,但是不保證原子性。
6.volatile關鍵字的語義分析
(1)保證可見性,volatile修飾的共享變數被修改時,其他處理器能立刻嗅探到共享變數值的改變
(2)保證有序性:根據happens-before原則可知,當變數使用volatile修飾時,程式程式碼前後的位置不能發生指令重排和提取。
(3)volatile底層採用彙編的lock字首指令鎖定共享變數記憶體地址的快取行,從而控制併發的安全性(輕量級synchronized)
7.volatile使用場景以及和synchronized的區別
使用場景:1.標誌狀態 2.DCL--雙重檢測鎖(單例模式) 3.保證可見性、順序性
區別:
1.使用上:volatile修飾變數 synchronized修飾方法或者程式碼塊
2.原子性的保證 volatile不保證原子性 synchronized可以保證原子性
3.可見性保證機制不同 volatile通過彙編的lock字首指令 synchronized使用Monitor屬性(Moniterentet 入口 Moniterexit--出口(包含異常))
4.有序性保證的鎖的粒度 volatile粒度小,synchronized粒度大
5.其他 volatile不會引起執行緒阻塞 synchronized會引起執行緒的阻塞
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。