9

JLSでは、§17.4.5。Happens-before Order、それは言う

プログラムが正しく同期されるのは、すべての連続した一貫した実行にデータ競合がない場合のみです。

正しく同期されたプログラムはまだデータ競合を許可しますか?(パート I) の議論によると、次の結論が得られます。

プログラムが正しく同期され、データ競合が発生する可能性があります。

2 つの結論の組み合わせは、次のような例が存在する必要があることを意味します。

プログラムのすべての順次一貫性のある実行にはデータ競合がありませんが、そのようなプログラムの通常の実行 (順次一貫性のある実行以外の実行) にはデータ競合が含まれます。

よく考えてみましたが、そのようなコード サンプルはまだ見つかりません。あなたは?

4

1 に答える 1

8

「プログラムが正しく同期され、データ競合が発生する可能性がある」というのは真実ではありません。その議論の assylias による例は、正しく同期されていません。高レベルの機能的な観点からは正しいです。含まれているデータ競合は、バグとしては現れません。これはいわゆる「無害な」データ競合ですが、JLS の定義を議論する際には関係ありません。

逐次一貫性のある実行にデータ競合が含まれないプログラムは、逐次一貫性の有無にかかわらず、どの実行においてもデータ競合を含まないことが保証されます。JLSが言うように、

これは、プログラマにとって非常に強力な保証です。プログラマーは、コードにデータ競合が含まれていることを判断するために並べ替えについて考える必要はありません。したがって、コードが正しく同期されているかどうかを判断する際に、並べ替えについて考える必要はありません。コードが正しく同期されていることが確認できれば、プログラマーは並べ替えが自分のコードに影響することを心配する必要はありません。

したがって、正しく同期されたプログラムの定義は、プログラマーへの礼儀として、順次一貫性のある実行のみに絞り込まれていることに注意してください。これにより、プログラマーは、順次一貫性のある実行のみが、プログラマーが推論する必要がある唯一のものであり、他のすべての実行についても考慮する必要があるという強力な保証が与えられます。自動的に同じ保証が得られます。

アップデート

JMM で使用される用語は簡単に理解できず、微妙な誤解が後で深い誤解につながります。したがって、これらを心に留めておいてください:

  • 実行は、単なる一連のスレッド間アクションです。それにアプリオリな順序はありません。特に時間順はありません

これは直観に反する定義なので、注意が必要です。 executionと言うたびに、 action のバッグを想像する必要があります。半順序を定義するときはいつでも、いくつかのバッグが並んでいると想像する必要があります。

  • プログラムには、アクションを実行するための命令が含まれています。そのような各命令は、ゼロ回以上実行でき、特定の実行にゼロ回以上の異なるアクションを提供します。
  • 実行には、すべてのアクションの合計順序である実行順序がある場合とない場合があります。
  • すべての共有変数が揮発性である場合、順次一貫性のある実行が得られます。この種の実行には、常に明確な実行順序があります。
  • 順次矛盾する実行は、実際のプログラムの実行です。非揮発性変数が関与し、コンパイラが読み取りと書き込みを並べ替えます。キャッシュ、スレッドローカル ストアなどがあります。
  • 同期順序は、実行によって行われたすべての同期アクションの合計順序です。実行自体に関しては、すべてのアクションが同期アクションであるとは限らないため、まだ部分的な順序です。最も顕著なのは、不揮発性変数の読み取りと書き込みです。シーケンシャルに一貫性があるかどうかに関係なく、すべての実行には明確な同期順序があります。
  • 同様に、先行発生順序は、プログラムの特定の実行に対して定義され、プログラム順序による同期順序の推移閉包として導出されます。

すべての共有変数が揮発性である場合、同期順序は合計順序になり、実行順序の定義を満たすことに注意してください。このようにして、別の角度から、そのようなプログラムのすべての実行は順次一貫性があるという結論に至ります。

データ競合の定義における JLS バグの根底に到達するために深く掘り下げました。

「プログラムに、先行発生関係によって順序付けられていない 2 つの競合するアクセス (§17.4.1) が含まれている場合、データ競合が含まれていると言われます。」

まず、データ競合を含むのはプログラムではなく、プログラムの実行です。Java メモリ モデルを定義した元の論文を参照すると、次のように修正されていることがわかります。

「 xyの2 つのアクセスが、プログラムの実行中にデータ競合を形成するのは、それらが異なるスレッドからのものであり、それらが衝突し、事前発生によって順序付けられていない場合です。」

ただし、これにより、揮発性変数に対するアクションがデータ競合として定義されたままになります。次の事前発生グラフを検討してください。

Thread W        w1 ----> w2
                |
                 \
Thread R     r0 ----> r1

r1は書き込みw1を監視します。その前に別の読み取りr0があり、書き込みの後に別の読み取りw2が続きました。ここで、 r0w1またはw2の間にパスがないことに注意してください。同様にr1w2の間。これらはすべて、定義によるデータ競合の例です。

しかし、さらに深く掘り下げると、memoryModel メーリング リストでこの投稿を見つけました。「データ競合は、前発生順で順序付けされていない不揮発性変数に対する競合するアクションとして定義する必要があります」と書かれています。その追加によってのみ抜け穴は塞がりますが、これはまだ公式の JLS リリースには入っていません。

于 2012-08-19T06:32:03.030 に答える