これらの用語は、並行プログラミングのコンテキストで使用されることがよくあります。それらは同じものですか、それとも異なりますか?
4 に答える
いいえ、それらは同じものではありません。それらは互いのサブセットではありません。それらは互いに必要条件でも十分条件でもありません。
データ競合の定義は非常に明確であるため、その検出は自動化できます。データ競合は、異なるスレッドからの 2 つの命令が同じメモリ位置にアクセスし、これらのアクセスの少なくとも 1 つが書き込みであり、これらのアクセス間で特定の順序を要求する同期がない場合に発生します。
競合状態はセマンティック エラーです。これは、プログラムの誤った動作につながるイベントのタイミングまたは順序で発生する欠陥です。多くの競合状態はデータ競合によって引き起こされる可能性がありますが、これは必須ではありません。
x が共有変数である次の簡単な例を考えてみましょう。
Thread 1 Thread 2
lock(l) lock(l)
x=1 x=2
unlock(l) unlock(l)
この例では、スレッド 1 と 2 からの x への書き込みはロックによって保護されているため、実行時にロックが取得される順序によって強制される何らかの順序で常に発生します。つまり、書き込みの原子性を壊すことはできません。どの実行においても、2 つの書き込みの間に常に発生前の関係があります。アプリオリに、どの書き込みが他の書き込みの前に発生するかを知ることはできません。
ロックはこれを提供できないため、書き込み間に決まった順序はありません。プログラムの正確性が損なわれる場合、たとえばスレッド 2 による x への書き込みの後にスレッド 1 での x への書き込みが続く場合、技術的にはデータ競合はありませんが、競合状態があると言います。
データ競合よりも競合状態を検出する方がはるかに便利です。ただし、これを達成することも非常に困難です。
逆の例を構築することも簡単です。このブログ投稿でも、簡単な銀行取引の例を使用して、違いを非常によく説明しています。
ウィキペディアによると、「競合状態」という用語は、最初の電子論理ゲートの時代から使用されています。Java のコンテキストでは、競合状態は、ファイル、ネットワーク接続、スレッド プールからのスレッドなど、あらゆるリソースに関係する可能性があります。
「データ競合」という用語は、JLSによって定義された特定の意味のために予約するのが最適です。
最も興味深いケースは、次の単純な例のように、データ競合に非常に似ていますが、データ競合ではない競合状態です。
class Race {
static volatile int i;
static int uniqueInt() { return i++; }
}
揮発性であるためi
、データ競合はありません。i
ただし、プログラムの正確性の観点からは、 read 、 writeの 2 つの操作の非アトミック性による競合状態が発生しますi+1
。複数のスレッドが から同じ値を受け取る場合がありますuniqueInt
。
いいえ、それらは異なり、どちらもサブセットではありません。その逆も同様です。
競合状態という用語は、関連する用語であるデータ競合と混同されることがよくあります。データ競合は、共有された非最終フィールドへのすべてのアクセスを調整するために同期が使用されていない場合に発生します。両方のスレッドが同期を使用しない場合、スレッドが次に別のスレッドによって読み取られる可能性のある変数を書き込むか、別のスレッドによって最後に書き込まれた可能性がある変数を読み取ると、常にデータ競合のリスクがあります。データ競合のあるコードには、Java メモリ モデルの下で定義された有用なセマンティクスがありません。すべての競合状態がデータ競合であるとは限らず、すべてのデータ競合が競合状態であるとは限りませんが、どちらも並行プログラムが予期しない方法で失敗する可能性があります。
Joshua Bloch & Co. による優れた書籍 - Java Concurrency in Practice から引用