volatile是否能保證陣列中元素的可見性?
阿新 • • 發佈:2018-12-23
在javaeye有位朋友問了我一個非常好的問題。
問題
一個執行緒向volatile的陣列中設定值,而另一個執行緒向volatile的陣列中讀取。
比如seg.setValue(2),隨後另一個執行緒呼叫seg.getValue(2),前一個執行緒設定的值對讀取的執行緒是可見的嗎?
我看書上說volatile的陣列只針對陣列的引用具有volatile的語義,而不是它的元素。
ConcurrentHashMap中也有這樣的程式碼,我很疑惑,希望得到你的解答,謝謝。
public class Seg { private volatile Object[] tabs = new Object[10]; public void setValue(int index) { tabs[index] = new Object(); } public Object getValue(int index) { return tabs[index]; } }
我的回答
我做了實驗證實這句話是正確的,“volatile的陣列只針對陣列的引用具有volatile的語義,而不是它的元素”。
測試程式碼如下:
private static volatile Object[] tabs = new Object[10];
public static void main(String[] args) {
tabs[0]=1;
tabs=new Object[10];
}
編譯成彙編語句如下
Java HotSpot(TM) Client VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output CompilerOracle: compileonly *ArrayTest.main Loaded disassembler from D:\java\jdk1.6.0_33\jre\bin\client\hsdis-i386.dll Decoding compiled method 0x009ec7c8: Code: [Disassembling for mach='i386'] [Entry Point] [Verified Entry Point] [Constants] # {method} 'main' '([Ljava/lang/String;)V' in 'volatileTest/ArrayTest' # parm0: ecx = '[Ljava/lang/String;' # [sp+0x30] (sp of caller) 0x009ec8e0: mov %eax,-0x3000(%esp) 0x009ec8e7: push %ebp 0x009ec8e8: sub $0x28,%esp 0x009ec8eb: mov $0x32aa1eb0,%esi ; {oop('volatileTest/ArrayTest')} 0x009ec8f0: mov 0x150(%esi),%edi ;*getstatic tabs ; - volatileTest.ArrayTest::[email protected] (line 7) 0x009ec8f6: mov $0x1,%ecx ;*invokestatic valueOf ; - volatileTest.ArrayTest::[email protected] (line 7) 0x009ec8fb: mov %esi,0x14(%esp) 0x009ec8ff: mov %edi,0x10(%esp) 0x009ec903: call 0x009ad340 ; OopMap{[20]=Oop [16]=Oop off=40} ;*invokestatic valueOf ; - volatileTest.ArrayTest::[email protected] (line 7) ; {static_call} 0x009ec908: mov 0x10(%esp),%edi 0x009ec90c: lea 0xc(%edi),%ebx 0x009ec90f: cmpl $0x0,0x8(%edi) ; implicit exception: dispatches to 0x009eca07 0x009ec916: jbe 0x009eca11 0x009ec91c: cmp $0x0,%eax 0x009ec91f: je 0x009ec960 0x009ec925: mov 0x4(%edi),%edx ; implicit exception: dispatches to 0x009eca1d 0x009ec928: mov 0x4(%eax),%esi 0x009ec92b: mov 0x88(%edx),%edx 0x009ec931: cmp %edx,%esi 0x009ec933: je 0x009ec960 0x009ec939: mov 0x10(%edx),%edi 0x009ec93c: cmp (%esi,%edi,1),%edx 0x009ec93f: je 0x009ec960 0x009ec945: cmp $0x14,%edi 0x009ec948: jne 0x009eca22 0x009ec94e: push %esi 0x009ec94f: push %edx 0x009ec950: call 0x009ebb40 ; {runtime_call} 0x009ec955: pop %esi 0x009ec956: pop %edx 0x009ec957: cmp $0x0,%edx 0x009ec95a: je 0x009eca22 0x009ec960: mov %eax,(%ebx) 0x009ec962: shr $0x9,%ebx 0x009ec965: movb $0x0,0x28eb100(%ebx) ;*aastore ; - volatileTest.ArrayTest::[email protected] (line 7) 0x009ec96c: mov $0xa,%ebx 0x009ec971: mov $0x37a0e380,%edx ; {oop('java/lang/Object'[])} 0x009ec976: mov %ebx,%edi 0x009ec978: cmp $0xffffff,%ebx 0x009ec97e: ja 0x009eca27 0x009ec984: mov $0x13,%esi 0x009ec989: lea (%esi,%ebx,4),%esi 0x009ec98c: and $0xfffffff8,%esi 0x009ec98f: mov %fs:0x0(,%eiz,1),%ecx 0x009ec997: mov -0xc(%ecx),%ecx 0x009ec99a: mov 0x34(%ecx),%eax 0x009ec99d: lea (%eax,%esi,1),%esi 0x009ec9a0: cmp 0x3c(%ecx),%esi 0x009ec9a3: ja 0x009eca27 0x009ec9a9: mov %esi,0x34(%ecx) 0x009ec9ac: sub %eax,%esi 0x009ec9ae: movl $0x1,(%eax) 0x009ec9b4: mov %edx,0x4(%eax) 0x009ec9b7: mov %ebx,0x8(%eax) 0x009ec9ba: sub $0xc,%esi 0x009ec9bd: je 0x009ec9e3 0x009ec9c3: xor %ebx,%ebx 0x009ec9c5: shr $0x3,%esi 0x009ec9c8: jae 0x009ec9d8 0x009ec9ce: mov %ebx,0xc(%eax,%esi,8) 0x009ec9d2: je 0x009ec9e3 0x009ec9d8: mov %ebx,0x8(%eax,%esi,8) 0x009ec9dc: mov %ebx,0x4(%eax,%esi,8) 0x009ec9e0: dec %esi 0x009ec9e1: jne 0x009ec9d8 0x009ec9e3: mov 0x14(%esp),%esi 0x009ec9e7: mov %eax,0x150(%esi) 0x009ec9ed: shr $0x9,%esi 0x009ec9f0: movb $0x0,0x28eb100(%esi) 0x009ec9f7: lock addl $0x0,(%esp) ;*putstatic tabs ; - volatileTest.ArrayTest::[email protected] (line 9) 0x009ec9fc: add $0x28,%esp 0x009ec9ff: pop %ebp 0x009eca00: test %eax,0x950100 ; {poll_return} 0x009eca06: ret 0x009eca07: call 0x009ea4d0 ; OopMap{[20]=Oop eax=Oop edi=Oop off=300} ;*aastore ; - volatileTest.ArrayTest::[email protected] (line 7) ; {runtime_call} 0x009eca0c: call 0x009ea4d0 ; OopMap{[20]=Oop eax=Oop edi=Oop off=305} ;*aastore ; - volatileTest.ArrayTest::[email protected] (line 7) ; {runtime_call} 0x009eca11: movl $0x0,(%esp) 0x009eca18: call 0x009ea1d0 ; OopMap{[20]=Oop eax=Oop edi=Oop off=317} ;*aastore ; - volatileTest.ArrayTest::[email protected] (line 7) ; {runtime_call} 0x009eca1d: call 0x009ea4d0 ; OopMap{[20]=Oop eax=Oop off=322} ;*aastore ; - volatileTest.ArrayTest::[email protected] (line 7) ; {runtime_call} 0x009eca22: call 0x009eb850 ; OopMap{[20]=Oop eax=Oop off=327} ;*aastore ; - volatileTest.ArrayTest::[email protected] (line 7) ; {runtime_call} 0x009eca27: call 0x009eb1c0 ; OopMap{[20]=Oop off=332} ;*anewarray ; - volatileTest.ArrayTest::[email protected] (line 9) ; {runtime_call} 0x009eca2c: jmp 0x009ec9e3 0x009eca2e: nop 0x009eca2f: nop 0x009eca30: mov %fs:0x0(,%eiz,1),%esi 0x009eca38: mov -0xc(%esi),%esi 0x009eca3b: mov 0x184(%esi),%eax 0x009eca41: movl $0x0,0x184(%esi) 0x009eca4b: movl $0x0,0x188(%esi) 0x009eca55: add $0x28,%esp 0x009eca58: pop %ebp 0x009eca59: jmp 0x009bb5c0 ; {runtime_call} 0x009eca5e: hlt 0x009eca5f: hlt [Stub Code] 0x009eca60: nop ; {no_reloc} 0x009eca61: nop 0x009eca62: mov $0x0,%ebx ; {static_stub} 0x009eca67: jmp 0x009eca67 ; {runtime_call} [Exception Handler] 0x009eca6c: call 0x009eb600 ; {runtime_call} 0x009eca71: push $0x6db038d4 ; {external_word} 0x009eca76: call 0x009eca7b 0x009eca7b: pusha 0x009eca7c: call 0x6da09de0 ; {runtime_call} 0x009eca81: hlt [Deopt Handler Code] 0x009eca82: push $0x9eca82 ; {section_word} 0x009eca87: jmp 0x009ad970 ; {runtime_call}
可以看到line 7給陣列的某個元素賦值時沒有lock字首的指令。
而修改陣列line 9才有lock字首的指令。
0x009ec9f7: lock addl $0x0,(%esp) ;*putstatic tabs ; - volatileTest.ArrayTest::[email protected] (line 9)