アップデート:
興味のある方は、このバグが対処され、Java 7u6 ビルド b14 で修正されました。ここでバグレポート/修正を確認できます
元の回答
メモリの可視性/順序の観点から考えるときは、その発生前の関係について考える必要があります。の重要な前提条件b != 0
は ですa == 1
。その場合a != 1
、b は 0 または 1 のいずれかになります。
スレッドa == 1
が を参照すると、そのスレッドは を参照することが保証されますb == 1
。
OPの例では、Java 5以降、while(a == 0)
ブレークアウトbが1であることが保証されると
編集:
シミュレーションを何度も実行しましたが、出力が表示されませんでした。
どの OS、Java バージョン、および CPU でテストしていますか?
私はWindows 7、Java 1.6_24を使用しています(_31で試しています)
編集2:
OPとWalter Laanへの称賛 - 私にとっては、64ビットのWindows 7で(ただし、除外されない場合があります)、64ビットのJavaから32ビットのJavaに切り替えたときにのみ発生しました。
編集3:
への割り当てtt
、またはむしろの staticget はb
大きな影響を与えるようです (これを証明するために、 を削除するint tt = b;
と、常に機能するはずです。
b
intoのロードによりtt
、フィールドがローカルに保存され、if coniditonal (その値への参照 not tt
) で使用されるようです。したがって、b == 0
が true の場合は、おそらくローカル ストア先tt
が 0 だったことを意味します (この時点で、 local に 1 を割り当てる競争tt
です)。これは、クライアントが設定された 32 ビット Java 1.6 & 7 にのみ当てはまるようです。
2 つの出力アセンブリを比較したところ、すぐに違いがわかりました。(これらはスニペットであることに注意してください)。
これは「エラー」を出力しました
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
この例では、最初のエントリは「エラー」を出力した実行からのものであり、2 番目はそうでないものからのものです。
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% の可能性があるように見えることがわかりました。