私はこれらの用語が並行プログラミングの文脈で使用されているのをよく見ます。それらは同じものですか、違うものですか?
いいえ、それらは同じものではありません。それらはお互いのサブセットではありません。また、これらは互いに必要な条件でも十分な条件でもありません。
データ競合の定義は非常に明確であるため、その検出は自動化できます。異なるスレッドからの2つの命令が同じメモリ位置にアクセスすると、データ競合が発生します。これらのアクセスの少なくとも1つは書き込みであり、同期を強制するものではありませんanyこれらのアクセス間の特定の順序。
競合状態はセマンティックエラーです。これは、イベントのタイミングまたは順序付けで発生する欠陥であり、プログラムの誤動作につながります。多くの競合状態は、データの競合によって発生する可能性がありますが、これは必須ではありません。
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への書き込みが続く場合、技術的にはデータの競合はありませんが、競合状態があると言います。
データの競合よりも競合状態を検出する方がはるかに便利です。ただし、これも非常に困難です。
逆の例を構築するのも簡単です。 This ブログの投稿でも、簡単な銀行取引の例で違いを非常によく説明しています。
ウィキペディアによると、「競合状態」という用語は、最初の電子論理ゲートの時代から使用されています。 Javaのコンテキストでは、競合状態は、ファイル、ネットワーク接続、スレッドプールのスレッドなど、あらゆるリソースに関係する可能性があります。
「データ競合」という用語は、 [〜#〜] jls [〜#〜] で定義される特定の意味のために最も予約されています。
最も興味深いケースは、データの競合に非常に似ている競合条件ですが、この単純な例のように、まだそうではありません。
class Race {
static volatile int i;
static int uniqueInt() { return i++; }
}
i
は揮発性であるため、データの競合はありません。ただし、プログラムの正確性の観点から、2つの操作の非アトミック性による競合状態があります。readi
、write i+1
。複数のスレッドがuniqueInt
から同じ値を受け取る場合があります。
いいえ、それらは異なり、どちらもサブセットの1つではなく、その逆でもありません。
競合状態という用語は、関連する用語データ競合と混同されることがよくあります。これは、共有非最終フィールドへのすべてのアクセスを調整するために同期が使用されない場合に発生します。スレッドが次に別のスレッドによって読み取られる可能性のある変数を書き込むか、両方のスレッドが同期を使用しない場合に別のスレッドによって最後に書き込まれた可能性のある変数を読み取ると、データ競合のリスクがあります。データ競合を伴うコードには、Java Memory Model。の下に有用な定義されたセマンティクスがありません。すべての競合状態がデータ競合であるわけではありません。予測不可能な方法で。
優秀な本から引用-Joshua Bloch&Co.による実践的なJava並行性