この記事では主に Java マルチスレッド デッドロックの詳細な説明と簡単な例を紹介します。必要な方は参考にしてください。
デッドロックとは、複数のスレッドが同時にブロックされ、そのうちの 1 つまたはすべてがブロックされる状況です。リソースが解放されるのを待っています。スレッドは無期限にブロックされるため、プログラムは正常に終了できません。
Java デッドロックに必要な 4 つの条件:
1> 相互排他的使用、つまり、リソースが 1 つのスレッドによって使用 (占有) されている場合、他のスレッドはそれを使用できません
2> 非プリエンプション、リソースrequest 所有者はリソース占有者からリソースを強制的に奪うことはできず、リソースを解放できるのはリソース占有者が自らの意志でのみです。
3> リクエストと維持、つまり、リソース要求者が元のリソースに仲間を維持しながら他のリソースをリクエストする場合。
4>ループ待機、つまり待機キューがある: P1はP2のリソースを占有し、P2はP3のリソースを占有し、P3はP1のリソースを占有します。これにより、待機ループが作成されます。
上記の 4 つの条件が当てはまる場合、デッドロックが形成されます。もちろん、デッドロックの場合、上記の条件のいずれかが破られれば、デッドロックは解消されます。 Java コードを使用してデッドロックの発生をシミュレートしてみましょう。
デッドロックの問題を解決する方法は、同期を使用する方法と、Lock の明示的なロック実装を使用する方法です。
そして、ロックが不適切に使用され、複数の オブジェクトを同時にロックする必要がある場合、次のようなデッドロック状況が発生します:
/* author by w3cschool.cc LockTest.java */ import java.util.Date; public class LockTest { public static String obj1 = "obj1"; public static String obj2 = "obj2"; public static void main(String[] args) { LockA la = new LockA(); new Thread(la).start(); LockB lb = new LockB(); new Thread(lb).start(); } } class LockA implements Runnable{ public void run() { try { System.out.println(new Date().toString() + " LockA 开始执行"); while(true){ synchronized (LockTest.obj1) { System.out.println(new Date().toString() + " LockA 锁住 obj1"); Thread.sleep(3000); // 此处等待是给B能锁住机会 synchronized (LockTest.obj2) { System.out.println(new Date().toString() + " LockA 锁住 obj2"); Thread.sleep(60 * 1000); // 为测试,占用了就不放 } } } } catch (Exception e) { e.printStackTrace(); } } } class LockB implements Runnable{ public void run() { try { System.out.println(new Date().toString() + " LockB 开始执行"); while(true){ synchronized (LockTest.obj2) { System.out.println(new Date().toString() + " LockB 锁住 obj2"); Thread.sleep(3000); // 此处等待是给A能锁住机会 synchronized (LockTest.obj1) { System.out.println(new Date().toString() + " LockB 锁住 obj1"); Thread.sleep(60 * 1000); // 为测试,占用了就不放 } } } } catch (Exception e) { e.printStackTrace(); } } }
上記のコードを実行した出力結果は次のとおりです:
Tue May 05 10:51:06 CST 2015 LockB 开始执行 Tue May 05 10:51:06 CST 2015 LockA 开始执行 Tue May 05 10:51:06 CST 2015 LockB 锁住 obj2 Tue May 05 10:51:06 CST 2015 LockA 锁住 obj1
この時点でデッドロックが生成されます。
この問題を解決するために、明示的なロック解除を使用せず、セマフォを使用して制御します。
セマフォは、リソースにアクセスできるスレッドの数を制御できます。ここでは、ロックと同様に、1 つのスレッドによってのみアクセスできるように指定します。セマフォでは取得のタイムアウト期間を指定でき、このタイムアウト期間に基づいて追加の処理を行うことができます。
正常に取得できない場合は、通常は再試行するか、試行回数を指定するか、すぐに終了することができます。
次のコードを見てみましょう:
/* author by w3cschool.cc UnLockTest.java */ import java.util.Date; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; public class UnLockTest { public static String obj1 = "obj1"; public static final Semaphore a1 = new Semaphore(1); public static String obj2 = "obj2"; public static final Semaphore a2 = new Semaphore(1); public static void main(String[] args) { LockAa la = new LockAa(); new Thread(la).start(); LockBb lb = new LockBb(); new Thread(lb).start(); } } class LockAa implements Runnable { public void run() { try { System.out.println(new Date().toString() + " LockA 开始执行"); while (true) { if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println(new Date().toString() + " LockA 锁住 obj1"); if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println(new Date().toString() + " LockA 锁住 obj2"); Thread.sleep(60 * 1000); // do something }else{ System.out.println(new Date().toString() + "LockA 锁 obj2 失败"); } }else{ System.out.println(new Date().toString() + "LockA 锁 obj1 失败"); } UnLockTest.a1.release(); // 释放 UnLockTest.a2.release(); Thread.sleep(1000); // 马上进行尝试,现实情况下do something是不确定的 } } catch (Exception e) { e.printStackTrace(); } } } class LockBb implements Runnable { public void run() { try { System.out.println(new Date().toString() + " LockB 开始执行"); while (true) { if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println(new Date().toString() + " LockB 锁住 obj2"); if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println(new Date().toString() + " LockB 锁住 obj1"); Thread.sleep(60 * 1000); // do something }else{ System.out.println(new Date().toString() + "LockB 锁 obj1 失败"); } }else{ System.out.println(new Date().toString() + "LockB 锁 obj2 失败"); } UnLockTest.a1.release(); // 释放 UnLockTest.a2.release(); Thread.sleep(10 * 1000); // 这里只是为了演示,所以tryAcquire只用1秒,而且B要给A让出能执行的时间,否则两个永远是死锁 } } catch (Exception e) { e.printStackTrace(); } } }
上記のコード例の出力構造は次のとおりです:
Tue May 05 10:59:13 CST 2015 LockA 开始执行 Tue May 05 10:59:13 CST 2015 LockB 开始执行 Tue May 05 10:59:13 CST 2015 LockB 锁住 obj2 Tue May 05 10:59:13 CST 2015 LockA 锁住 obj1 Tue May 05 10:59:14 CST 2015LockB 锁 obj1 失败 Tue May 05 10:59:14 CST 2015LockA 锁 obj2 失败 Tue May 05 10:59:15 CST 2015 LockA 锁住 obj1 Tue May 05 10:59:15 CST 2015 LockA 锁住 obj2
[関連する推奨事項]
3. Geek Academy Java ビデオ チュートリアル
以上がJava開発で発生するデッドロックの解決策の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。