マルチスレッドプログラミングの概念を理解しようとしています。デッドロックとミューテックスの概念は知っていますが、次の質問に対する答えが見つかりません。ミューテックスを使用すると、デッドロックの問題がどのように発生する可能性がありますか?
4 に答える
Java でデッドロックを引き起こす方法の具体例を次に示します。2 つのスレッドを生成します。最初のものは でミューテックス ロックを取得し、1a
秒待ってから でロックを取得しようとしb
ます。2 番目は でロックを取得しb
、待機してから を取得しようとしa
ます。その結果、プログラムはデッドロックに入り、永久に実行されます。
public class Deadlock {
public static void main(String[] args) {
final Object a = new Object();
final Object b = new Object();
new Thread() {
@Override
public void run() {
synchronized (a) {
/* wait for a second to make it likely the other thread can acquire b */
try { Thread.sleep(1000); } catch (Exception e) { }
synchronized (b) {
System.out.println("Acquired a, then b.");
}
}
}
}.start();
new Thread() {
@Override
public void run() {
synchronized (b) {
/* wait for a second to make it likely the other thread can acquire a */
try { Thread.sleep(1000); } catch (Exception e) { }
synchronized (a) {
System.out.println("Acquired b, then a.");
}
}
}
}.start();
}
}
このコードは、デッドロックに入ることが保証されていないことに注意してください。スレッドスケジューラは、2 番目のスレッドを開始する前に最初のスレッドを最後まで実行する権利を完全に持っています。この例では待ち時間が非常に長いため、システムがデッドロックに陥ることはほぼ確実ですが、1 秒間スリープする代わりに、両方のスレッドがそれぞれのロックを取得する前に可変時間の長さの計算を行っていた場合、次のいずれかが行われます。ランダムに発生する可能性があります:
- デッドロック
"Acquired a, then b."
、 に続く"Acquired b, then a."
"Acquired b, then a."
、 に続く"Acquired a, then b."
これを防ぐにはどうすればよいですか?
- スレッドを回避できる場合は、スレッドを完全に使用しないでください。
- スレッド間でリソースを共有する代わりに、各スレッドが独自のデータを操作し、相互に不変メッセージを送信します。
- どうしても共有リソースを使用する必要がある場合は、使用するミューテックス ロックの数を最小限に抑えてください。すべてが同じオブジェクトで同期する場合、ミューテックス ロックは 1 つだけであり、デッドロックは発生しません。
- 絶対に多数のミューテックス ロックが必要で、スレッドがそれらの組み合わせを取得する必要がある場合は、次の手順を実行します。ロックの全体的な順序を定義する関数を記述します。次に、複数のミューテックスを含む操作を実行する必要があるときはいつでも、関連するミューテックスのリストを作成し、それらを並べ替えてから、操作の開始時に並べ替えられた順序で入力します。
java.util.concurrentパッケージを見てください。本当に毛むくじゃらの部分の多くをケアするグッズがたくさん含まれています。
また、予期しない場所でスレッドが切り替わる可能性があることは、いくら強調してもしすぎることはありません。よくある間違いは、各スレッドを 1 行ずつ見て、行がインターリーブするさまざまな方法を想像することです。しかし、それだけでは不十分です。スレッドは、一部のライブラリ内の奥深くにネストされた関数呼び出しの途中で切り替えられる可能性があります。
ロック順序のデッドロックを回避するために複数のロックを取得する必要がある場合は、 Lockオブジェクトを使用します。この場合、LockオブジェクトのメソッドtryLock()は、値falseですぐに戻ります。その結果、1つのスレッドだけが同時に両方のロックを保持します。同期されたコードの場合、スレッドは次のロックを待つ間、1つのオブジェクトへのロックの保持をブロックします。以下のサンプルコードを参照してください。
while (true) {
if (a.lock.tryLock()) {
try {
if (b.lock.tryLock()) {
try {
// manipulate protected state
} finally {
b.lock.unlock();
}
}
} finally {
a.lock.unlock();
}
}
}
デッドロックは、1 つのスレッドがリソース A をロックし、2 番目のスレッドによってリソース B がロックされるのを待っている状況です。同時に、2番目のスレッドは、2番目のスレッドによってロックされたリソースBを待機している最初のスレッドによってロックされたリソースAを待機しています.... :)
解決策は、スレッド モデルの「スマートな」設計、キューを使用したスレッドの分離、または不変オブジェクトを使用して同期が不要になるようにすることなどによって、この状況を回避することです。
Mutex は同期ブロックの鍵です。同期ブロック/ミューテックス変数を不適切に使用すると、デッドロックが発生します。