java中volatile为什么可见性_为什么Java 5+中的volatile不能确保另一个线程的可见性?...

小编典典

更新:

对于有兴趣的人,此错误已得到解决,并已在Java 7u6 build b14中修复。您可以在此处查看错误报告/修复程序

原始答案

在考虑内存可见性/顺序时,您需要考虑其事前发生的关系。的重要前提b != 0是a == 1。如果是,a != 1则b可以为0或1。

一旦看到a == 1线程,便保证该线程看到b == 1。

在OP示例中,在Java 5之后,一旦while(a == 0)突破b保证为1

编辑:

我多次运行模拟,但没有看到您的输出。

您在什么操作系统,Java版本和CPU下进行测试?

我在Windows 7,Java 1.6_24上(尝试_31)

编辑2:

对OP和Walter Laan表示敬意-对我来说,只有在我从64位Java切换到32位Java时,才发生这种情况,但不一定排除在64位Windows 7中。

编辑3:

指派给tt,或者说静态化b似乎有很大的影响(以证明删除了int tt = b;,它应该一直有效。

它出现的负载b成tt将本地存储的字段,它然后将在如果coniditonal被使用(参考到该值不tt)。因此,如果b ==

0为true,则可能意味着to的本地存储为tt0(这是将1分配给local的竞赛tt)。这似乎仅对于带有客户端集的32位Java

1.6和7是正确的。

我比较了两个输出组件,直接的区别就在这里。(请记住,这些都是片段)。

这样印“错误”

0x021dd753: test %eax,0x180100 ; {poll}

0x021dd759: cmp $0x0,%ecx

0x021dd75c: je 0x021dd748 ;*ifeq

; - Test$1::run@7 (line 13)

0x021dd75e: cmp $0x0,%edx

0x021dd761: jne 0x021dd788 ;*ifne

; - Test$1::run@13 (line 17)

0x021dd767: nop

0x021dd768: jmp 0x021dd7b8 ; {no_reloc}

0x021dd76d: xchg %ax,%ax

0x021dd770: jmp 0x021dd7d2 ; implicit exception: dispatches to 0x021dd7c2

0x021dd775: nop ;*getstatic out

; - Test$1::run@16 (line 18)

0x021dd776: cmp (%ecx),%eax ; implicit exception: dispatches to 0x021dd7dc

0x021dd778: mov $0x39239500,%edx ;*invokevirtual println

这没有打印“错误”

0x0226d763: test %eax,0x180100 ; {poll}

0x0226d769: cmp $0x0,%edx

0x0226d76c: je 0x0226d758 ;*ifeq

; - Test$1::run@7 (line 13)

0x0226d76e: mov $0x341b77f8,%edx ; {oop('Test')}

0x0226d773: mov 0x154(%edx),%edx ;*getstatic b

; - Test::access$0@0 (line 3)

; - Test$1::run@10 (line 17)

0x0226d779: cmp $0x0,%edx

0x0226d77c: jne 0x0226d7a8 ;*ifne

; - Test$1::run@13 (line 17)

0x0226d782: nopw 0x0(%eax,%eax,1)

0x0226d788: jmp 0x0226d7ed ; {no_reloc}

0x0226d78d: xchg %ax,%ax

0x0226d790: jmp 0x0226d807 ; implicit exception: dispatches to 0x0226d7f7

0x0226d795: nop ;*getstatic out

; - Test$1::run@16 (line 18)

0x0226d796: cmp (%ecx),%eax ; implicit exception: dispatches to 0x0226d811

0x0226d798: mov $0x39239500,%edx ;*invokevirtual println

在此示例中,第一个条目来自打印“错误”的运行,而第二个条目则来自未打印错误的运行。

似乎b在测试等于0之前,已正确加载和分配了工作运行。

0x0226d76e: mov $0x341b77f8,%edx ; {oop('Test')}

0x0226d773: mov 0x154(%edx),%edx ;*getstatic b

; - Test::access$0@0 (line 3)

; - Test$1::run@10 (line 17)

0x0226d779: cmp $0x0,%edx

0x0226d77c: jne 0x0226d7a8 ;*ifne

; - Test$1::run@13 (line 17)

当打印“错误”的运行加载了缓存的版本时 %edx

0x021dd75e: cmp $0x0,%edx

0x021dd761: jne 0x021dd788 ;*ifne

; - Test$1::run@13 (line 17)

对于那些对汇编程序有更多经验的人,请权衡:)

编辑4

应该是我的最后一个编辑,因为并发开发人员可以使用它,我在有和没有int tt = b;分配的情况下进行

了更多测试。我发现,当我将最大值从100增加到1000时int tt = b,包含时似乎有100%的错误率,而排除它时似乎有0%的机会。

2020-09-18

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值