Java-Beispiele - Deadlock und Lösungen

Java 实例Java-Beispiele

Ein Deadlock ist eine Situation, in der mehrere Threads gleichzeitig blockiert sind und einer oder alle von ihnen darauf warten, dass eine Ressource freigegeben wird. Da die Threads unbegrenzt blockiert sind, kann das Programm nicht normal beendet werden.

Vier notwendige Bedingungen für einen Deadlock in Java:

  • 1. Gegenseitiger Ausschluss: Wenn eine Ressource von einem Thread verwendet (belegt) wird, können andere Threads sie nicht verwenden.
  • 2. Nicht-Präemption: Ein Ressourcenanforderer kann die Ressource nicht gewaltsam vom Ressourcenbesitzer nehmen; die Ressource kann nur freiwillig vom Ressourcenbesitzer freigegeben werden.
  • 3. Halten und Anfordern: Ein Ressourcenanforderer hält die Ressourcen, die er bereits besitzt, weiter, während er andere Ressourcen anfordert.
  • 4. Zirkuläres Warten: Es gibt eine Warteschlange: P1 belegt die Ressource von P2, P2 belegt die Ressource von P3 und P3 belegt die Ressource von P1. Dadurch entsteht ein Wartezyklus.

Wenn alle vier oben genannten Bedingungen erfüllt sind, tritt ein Deadlock auf. Natürlich kann in einer Deadlock-Situation das Durchbrechen einer der oben genannten Bedingungen den Deadlock beseitigen. Der folgende Java-Code simuliert das Auftreten eines Deadlocks.

Die Methoden zur Lösung eines Deadlocks sind: eine besteht darin, synchronized zu verwenden, und die andere darin, die explizite Lock-Implementierung zu verwenden.

Wenn Sperren jedoch unsachgemäß verwendet werden und mehrere Objekte gleichzeitig gesperrt werden müssen, kann ein Deadlock auftreten, wie folgt:

LockTest.java-Datei

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 beginnt mit der Ausführung"); while(true){ synchronized (LockTest.obj1) { System.out.println(new Date().toString() + "LockA sperrt obj1"); Thread.sleep(3000); //Das Warten hier soll B eine Chance zum Sperren geben. synchronized (LockTest.obj2) { System.out.println(new Date().toString() + "LockA sperrt obj2"); Thread.sleep(60 * 1000); //Zu Testzwecken wird es, sobald belegt, nicht freigegeben. } } } } catch (Exception e) { e.printStackTrace(); } } } class LockB implements Runnable{ public void run() { try { System.out.println(new Date().toString() + "LockB beginnt mit der Ausführung"); while(true){ synchronized (LockTest.obj2) { System.out.println(new Date().toString() + "LockB sperrt obj2"); Thread.sleep(3000); //Das Warten hier soll A eine Chance zum Sperren geben. synchronized (LockTest.obj1) { System.out.println(new Date().toString() + "LockB sperrt obj1"); Thread.sleep(60 * 1000); //Zu Testzwecken wird es, sobald belegt, nicht freigegeben. } } } } catch (Exception e) { e.printStackTrace(); } } }

Der obige Code erzeugt die folgende Ausgabe:

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

An diesem Punkt tritt ein Deadlock auf.

Um dieses Problem zu lösen, verwenden wir keine expliziten Sperren; wir verwenden Semaphoren zur Steuerung.

Ein Semaphor kann steuern, wie viele Threads auf eine Ressource zugreifen können. Hier legen wir fest, dass nur ein Thread darauf zugreifen kann, wodurch etwas Ähnliches wie Sperren erreicht wird. Ein Semaphor kann auch ein Timeout für dessen Erwerb angeben, und wir können basierend auf diesem Timeout zusätzliche Verarbeitung durchführen.

Für Fälle, in denen der Erwerb nicht erfolgreich ist, besteht der übliche Ansatz darin, wiederholt erneut zu versuchen, eine Anzahl von Versuchen anzugeben oder sofort zu beenden.

Schauen wir uns den folgenden Code an:

UnLockTest.java-Datei

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 beginnt mit der Ausführung"); while (true) { if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println(new Date().toString() + "LockA sperrt obj1"); if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println(new Date().toString() + "LockA sperrt obj2"); Thread.sleep(60 * 1000); // etwas tun }else{ System.out.println(new Date().toString() + "LockA kann obj2 nicht sperren"); } }else{ System.out.println(new Date().toString() + "LockA kann obj1 nicht sperren"); } UnLockTest.a1.release(); //Release UnLockTest.a2.release(); Thread.sleep(1000); //Sofort versuchen; in realen Situationen ist ungewiss, ob etwas getan wird. } } catch (Exception e) { e.printStackTrace(); } } } class LockBb implements Runnable { public void run() { try { System.out.println(new Date().toString() + "LockB beginnt mit der Ausführung"); while (true) { if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println(new Date().toString() + "LockB sperrt obj2"); if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println(new Date().toString() + "LockB sperrt obj1"); Thread.sleep(60 * 1000); // etwas tun }else{ System.out.println(new Date().toString() + "LockB kann obj1 nicht sperren"); } }else{ System.out.println(new Date().toString() + "LockB kann obj2 nicht sperren"); } UnLockTest.a1.release(); //Release UnLockTest.a2.release(); Thread.sleep(10 * 1000); //Dies dient nur zur Demonstration, daher verwendet tryAcquire nur 1 Sekunde, und B muss A Ausführungszeit überlassen; andernfalls werden die beiden immer in einem Deadlock sein. } } catch (Exception e) { e.printStackTrace(); } } }

Die Ausgabe des obigen Beispielcodes ist:

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

Java 实例Java-Beispiele

Andere Erweiterungen