volatile是否能保证数组中元素的可见性?

在javaeye有位朋友问了我一个非常好的问题。

问题

一个线程向volatile的数组中设置值,而另一个线程向volatile的数组中读取。
比如seg.setValue(2),随后另一个线程调用seg.getValue(2),前一个线程设置的值对读取的线程是可见的吗?

我看书上说volatile的数组只针对数组的引用具有volatile的语义,而不是它的元素。

ConcurrentHashMap中也有这样的代码,我很疑惑,希望得到你的解答,谢谢。

01 public class Seg {
02 
03 private volatile Object[] tabs = new Object[10];
04 
05 public void setValue(int index) {
06 tabs[index] = new Object();
07}
08 
09 public Object getValue(int index) {
10 return tabs[index];
11}
12}

我的回答

我做了实验证实这句话是正确的,“volatile的数组只针对数组的引用具有volatile的语义,而不是它的元素”。

测试代码如下:
private static volatile Object[] tabs = new Object[10];

public static void main(String[] args) {
tabs[0]=1;

tabs=new Object[10];
}

编译成汇编语句如下

001Java HotSpot(TM) Client VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output
002CompilerOracle: compileonly *ArrayTest.main
003Loaded disassembler from D:\java\jdk1.6.0_33\jre\bin\client\hsdis-i386.dll
004Decoding compiled method 0x009ec7c8:
005Code:
006[Disassembling for mach='i386']
007[Entry Point]
008[Verified Entry Point]
009[Constants]
010# {method} 'main' '([Ljava/lang/String;)V' in 'volatileTest/ArrayTest'
011# parm0: ecx = '[Ljava/lang/String;'
012# [sp+0x30] (sp of caller)
0130x009ec8e0: mov %eax,-0x3000(%esp)
0140x009ec8e7: push %ebp
0150x009ec8e8: sub $0x28,%esp
0160x009ec8eb: mov $0x32aa1eb0,%esi ; {oop('volatileTest/ArrayTest')}
0170x009ec8f0: mov 0x150(%esi),%edi ;*getstatic tabs
018; - volatileTest.ArrayTest::main@0 (line 7)
0190x009ec8f6: mov $0x1,%ecx ;*invokestatic valueOf
020; - volatileTest.ArrayTest::main@5 (line 7)
0210x009ec8fb: mov %esi,0x14(%esp)
0220x009ec8ff: mov %edi,0x10(%esp)
0230x009ec903: call 0x009ad340 ; OopMap{[20]=Oop [16]=Oop off=40}
024;*invokestatic valueOf
025; - volatileTest.ArrayTest::main@5 (line 7)
026; {static_call}
0270x009ec908: mov 0x10(%esp),%edi
0280x009ec90c: lea 0xc(%edi),%ebx
0290x009ec90f: cmpl $0x0,0x8(%edi) ; implicit exception: dispatches to 0x009eca07
0300x009ec916: jbe 0x009eca11
0310x009ec91c: cmp $0x0,%eax
0320x009ec91f: je 0x009ec960
0330x009ec925: mov 0x4(%edi),%edx ; implicit exception: dispatches to 0x009eca1d
0340x009ec928: mov 0x4(%eax),%esi
0350x009ec92b: mov 0x88(%edx),%edx
0360x009ec931: cmp %edx,%esi
0370x009ec933: je 0x009ec960
0380x009ec939: mov 0x10(%edx),%edi
0390x009ec93c: cmp (%esi,%edi,1),%edx
0400x009ec93f: je 0x009ec960
0410x009ec945: cmp $0x14,%edi
0420x009ec948: jne 0x009eca22
0430x009ec94e: push %esi
0440x009ec94f: push %edx
0450x009ec950: call 0x009ebb40 ; {runtime_call}
0460x009ec955: pop %esi
0470x009ec956: pop %edx
0480x009ec957: cmp $0x0,%edx
0490x009ec95a: je 0x009eca22
0500x009ec960: mov %eax,(%ebx)
0510x009ec962: shr $0x9,%ebx
0520x009ec965: movb $0x0,0x28eb100(%ebx) ;*aastore
053; - volatileTest.ArrayTest::main@8 (line 7)
0540x009ec96c: mov $0xa,%ebx
0550x009ec971: mov $0x37a0e380,%edx ; {oop('java/lang/Object'[])}
0560x009ec976: mov %ebx,%edi
0570x009ec978: cmp $0xffffff,%ebx
0580x009ec97e: ja 0x009eca27
0590x009ec984: mov $0x13,%esi
0600x009ec989: lea (%esi,%ebx,4),%esi
0610x009ec98c: and $0xfffffff8,%esi
0620x009ec98f: mov %fs:0x0(,%eiz,1),%ecx
0630x009ec997: mov -0xc(%ecx),%ecx
0640x009ec99a: mov 0x34(%ecx),%eax
0650x009ec99d: lea (%eax,%esi,1),%esi
0660x009ec9a0: cmp 0x3c(%ecx),%esi
0670x009ec9a3: ja 0x009eca27
0680x009ec9a9: mov %esi,0x34(%ecx)
0690x009ec9ac: sub %eax,%esi
0700x009ec9ae: movl $0x1,(%eax)
0710x009ec9b4: mov %edx,0x4(%eax)
0720x009ec9b7: mov %ebx,0x8(%eax)
0730x009ec9ba: sub $0xc,%esi
0740x009ec9bd: je 0x009ec9e3
0750x009ec9c3: xor %ebx,%ebx
0760x009ec9c5: shr $0x3,%esi
0770x009ec9c8: jae 0x009ec9d8
0780x009ec9ce: mov %ebx,0xc(%eax,%esi,8)
0790x009ec9d2: je 0x009ec9e3
0800x009ec9d8: mov %ebx,0x8(%eax,%esi,8)
0810x009ec9dc: mov %ebx,0x4(%eax,%esi,8)
0820x009ec9e0: dec %esi
0830x009ec9e1: jne 0x009ec9d8
0840x009ec9e3: mov 0x14(%esp),%esi
0850x009ec9e7: mov %eax,0x150(%esi)
0860x009ec9ed: shr $0x9,%esi
0870x009ec9f0: movb $0x0,0x28eb100(%esi)
0880x009ec9f7: lock addl $0x0,(%esp) ;*putstatic tabs
089; - volatileTest.ArrayTest::main@14 (line 9)
0900x009ec9fc: add $0x28,%esp
0910x009ec9ff: pop %ebp
0920x009eca00: test %eax,0x950100 ; {poll_return}
0930x009eca06: ret
0940x009eca07: call 0x009ea4d0 ; OopMap{[20]=Oop eax=Oop edi=Oop off=300}
095;*aastore
096; - volatileTest.ArrayTest::main@8 (line 7)
097; {runtime_call}
0980x009eca0c: call 0x009ea4d0 ; OopMap{[20]=Oop eax=Oop edi=Oop off=305}
099;*aastore
100; - volatileTest.ArrayTest::main@8 (line 7)
101; {runtime_call}
1020x009eca11: movl $0x0,(%esp)
1030x009eca18: call 0x009ea1d0 ; OopMap{[20]=Oop eax=Oop edi=Oop off=317}
104;*aastore
105; - volatileTest.ArrayTest::main@8 (line 7)
106; {runtime_call}
1070x009eca1d: call 0x009ea4d0 ; OopMap{[20]=Oop eax=Oop off=322}
108;*aastore
109; - volatileTest.ArrayTest::main@8 (line 7)
110; {runtime_call}
1110x009eca22: call 0x009eb850 ; OopMap{[20]=Oop eax=Oop off=327}
112;*aastore
113; - volatileTest.ArrayTest::main@8 (line 7)
114; {runtime_call}
1150x009eca27: call 0x009eb1c0 ; OopMap{[20]=Oop off=332}
116;*anewarray
117; - volatileTest.ArrayTest::main@11 (line 9)
118; {runtime_call}
1190x009eca2c: jmp 0x009ec9e3
1200x009eca2e: nop
1210x009eca2f: nop
1220x009eca30: mov %fs:0x0(,%eiz,1),%esi
1230x009eca38: mov -0xc(%esi),%esi
1240x009eca3b: mov 0x184(%esi),%eax
1250x009eca41: movl $0x0,0x184(%esi)
1260x009eca4b: movl $0x0,0x188(%esi)
1270x009eca55: add $0x28,%esp
1280x009eca58: pop %ebp
1290x009eca59: jmp 0x009bb5c0 ; {runtime_call}
1300x009eca5e: hlt
1310x009eca5f: hlt
132[Stub Code]
1330x009eca60: nop ; {no_reloc}
1340x009eca61: nop
1350x009eca62: mov $0x0,%ebx ; {static_stub}
1360x009eca67: jmp 0x009eca67 ; {runtime_call}
137[Exception Handler]
1380x009eca6c: call 0x009eb600 ; {runtime_call}
1390x009eca71: push $0x6db038d4 ; {external_word}
1400x009eca76: call 0x009eca7b
1410x009eca7b: pusha
1420x009eca7c: call 0x6da09de0 ; {runtime_call}
1430x009eca81: hlt
144[Deopt Handler Code]
1450x009eca82: push $0x9eca82 ; {section_word}
1460x009eca87: jmp 0x009ad970 ; {runtime_call}

 

可以看到line 7给数组的某个元素赋值时没有lock前缀的指令。
而修改数组line 9才有lock前缀的指令。

10x009ec9f7: lock addl $0x0,(%esp) ;*putstatic tabs
2; - volatileTest.ArrayTest::main@14 (line 9)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值