デッドロックは場合によっては回避できる。この記事では、デッドロックを回避するための 3 つのテクニックを説明します。 Java でのデッドロックの回避に関する知識に興味がある友人は、この記事から学んでください
場合によっては、デッドロックを回避できる場合があります。この記事では、デッドロックを回避するための 3 つのテクニックを説明します:
1. ロック順序
2. ロック時間制限
3. ロック順序
複数のスレッドが必要な場合順序が異なると、デッドロックが発生しやすくなります。 すべてのスレッドが同じ順序でロックを取得することが保証できれば、デッドロックは発生しません。次の例を見てください:
Thread 1: lock A lock B Thread 2: wait for A lock C (when A locked) Thread 3: wait for A wait for B wait for C
たとえば、スレッド 2 とスレッド 3 は、ロック A を取得した後にのみ、ロック C の取得を試行できます (ロック A の取得は、ロック C を取得するための必須条件です)。スレッド 1 はすでにロック A を所有しているため、スレッド 2 と 3 はロック A が解放されるまで待つ必要があります。次に、B または C をロックする前に、A を正常にロックする必要があります。
順番にロックすることは、効果的なデッドロック防止メカニズムです。ただし、この方法では、使用される可能性のあるすべてのロックを事前に把握しておく (およびこれらのロックを適切に順序付けする) 必要がありますが、場合によってはそれが予測できないこともあります。
ロック時間制限
デッドロックを回避するもう 1 つの方法は、ロックを取得しようとするときにタイムアウトを追加することです。つまり、ロックを取得しようとするときにこの制限時間を超えた場合、スレッドはロックを放棄します。ロック要求。スレッドが指定された制限時間内に必要なすべてのロックを正常に取得できなかった場合、スレッドはロールバックして取得したすべてのロックを解放し、ランダムな期間待機してから再試行します。このランダムな待機時間により、他のスレッドに同じロックの取得を試行する機会が与えられ、アプリケーションはロックを取得せずに実行を継続できます (ロックがタイムアウトになった後は、実行を継続して他の作業を行ってから戻ることができます)前のロック ロジックを繰り返します)。
以下は、2 つのスレッドが異なる順序で同じ 2 つのロックを取得しようとし、タイムアウトが発生した後にロールバックして再試行する例を示しています:Thread 1 locks A Thread 2 locks B Thread 1 attempts to lock B but is blocked Thread 2 attempts to lock A but is blocked Thread 1's lock attempt on B times out Thread 1 backs up and releases A as well Thread 1 waits randomly (e.g. 257 millis) before retrying. Thread 2's lock attempt on A times out Thread 2 backs up and releases B as well Thread 2 waits randomly (e.g. 43 millis) before retrying.
状態
にあります。スレッド 2 が終了すると、スレッド 1 もこれら 2 つのロックを正常に取得できます (スレッド 1 が 2 つのロックを正常に取得する前にスレッド 2 または他のスレッドがロックの一部を取得しない限り)。
ロックのタイムアウトのため、このシナリオがデッドロックであるとは考えられないことに注意してください。また、ロックを取得したスレッド (他のスレッドがタイムアウトする原因となる) がタスクを完了するまでに時間がかかる可能性もあります。
さらに、同じバッチのリソースを同時に競合するスレッドが多数ある場合、タイムアウトとロールバックのメカニズムがある場合でも、これらのスレッドは繰り返し試行してもロックを取得できない可能性があります。スレッドが 2 つしかなく、リトライ タイムアウトが 0 ~ 500 ミリ秒に設定されている場合は、この現象は発生しない可能性がありますが、スレッドが 10 または 20 個ある場合は状況が異なります。これらのスレッドが同じ再試行回数を待機する可能性がはるかに高い (または問題が発生するほど近い) ためです。 (タイムアウトやリトライの仕組みは、同時に発生する競合を避けるためのものです。ただし、スレッド数が多い場合、複数のスレッドのタイムアウトが同じか近い可能性が高いため、競合が発生してもその後、タイムアウトが同じであるため、同時に再試行が開始され、その結果、新しいラウンドの競争が発生し、新たな問題が発生します)
current
パッケージにあるツールを使用する必要があります。カスタム ロック クラスの作成は複雑ではありませんが、この記事の範囲を超えています。
デッドロック検出は、より優れたデッドロック防止メカニズムであり、主に順次ロックが不可能でロック タイムアウトが実現できないシナリオを対象としています。 スレッドがロックを取得するたびに、スレッドおよびロック関連のデータ構造 (マップ、グラフなど) に記録されます。さらに、スレッドがロックを要求するたびに、そのロックもこのデータ構造に記録する必要があります。
スレッドがロックの要求に失敗した場合、スレッドはロック グラフを走査してデッドロックが発生したかどうかを確認できます。たとえば、スレッド A はロック 7 を要求しますが、この時点でロック 7 はスレッド B によって保持されています。このとき、スレッド A は、スレッド A が現在保持しているロックをスレッド B が要求したかどうかを確認できます。スレッド B にそのようなリクエストがある場合、デッドロックが発生しています (スレッド A はロック 1 を所有し、ロック 7 を要求します。スレッド B はロック 7 を所有し、ロック 1 を要求します)。
もちろん、デッドロックは一般に、2 つのスレッドがお互いのロックを保持するよりも複雑です。スレッド A はスレッド B を待機し、スレッド B はスレッド C を待機し、スレッド C はスレッド D を待機し、スレッド D はスレッド A を待機します。スレッド A がデッドロックを検出するには、スレッド B によって要求されたすべてのロックを段階的に検出する必要があります。スレッド B によって要求されたロックから開始して、スレッド A はスレッド C を見つけ、次にスレッド D を見つけます。そして、スレッド D によって要求されたロックがスレッド A 自体によって保持されていることがわかりました。これは、デッドロックが発生したことを認識するときです。
それでは、デッドロックが検出された場合、これらのスレッドは何をすべきでしょうか?
考えられるアプローチは、すべてのロックを解除し、ロールバックし、ランダムな期間待機してから再試行することです。これは単純なロック タイムアウトに似ていますが、ロック リクエストがタイムアウトしたためではなく、デッドロックが発生した場合にのみロールバックが発生する点が異なります。バックオフと待機はありますが、同じロックのバッチをめぐって競合するスレッドが多数ある場合、依然としてデッドロックが繰り返し発生します。
より良い解決策は、これらのスレッドに 優先度 を設定し、1 つ (またはいくつか) のスレッドをロールバックさせ、残りのスレッドはデッドロックが発生していないかのように必要なロックを保持し続けることです。これらのスレッドに割り当てられた優先順位が固定されている場合、同じスレッドの優先順位が常に高くなります。この問題を回避するために、デッドロックが発生したときにランダムな優先順位を設定できます。
以上がJava でのデッドロックを回避する方法に関するサンプル コードの共有の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。