2009-10-13 3 views
11

Ich lerne über Deadlocks in Java, und es gibt diese Beispielcode von Sun offizielle Tutorial:Frage zu Deadlock-Situation in Java

Alphonse und Gaston sind Freunde, und große Gläubigen in Höflichkeit. Eine strikte Regel der Höflichkeit ist, dass, wenn Sie an einen Freund verbeugen, müssen Sie gebeugt bleiben, bis Ihr Freund eine Chance hat, den Bogen zurückzubringen. Leider berücksichtigt diese Regel nicht die Möglichkeit, dass zwei Freunde zur gleichen Zeit sich verbeugen können.

public class Deadlock { 
    static class Friend { 
     private final String name; 
     public Friend(String name) { 
      this.name = name; 
     } 
     public String getName() { 
      return this.name; 
     } 
     public synchronized void bow(Friend bower) { 
      System.out.format("%s: %s has bowed to me!%n", 
        this.name, bower.getName()); 
      bower.bowBack(this); 
     } 
     public synchronized void bowBack(Friend bower) { 
      System.out.format("%s: %s has bowed back to me!%n", 
        this.name, bower.getName()); 
     } 
    } 

    public static void main(String[] args) { 
     final Friend alphonse = new Friend("Alphonse"); 
     final Friend gaston = new Friend("Gaston"); 
     new Thread(new Runnable() { 
      public void run() { alphonse.bow(gaston); } 
     }).start(); 
     new Thread(new Runnable() { 
      public void run() { gaston.bow(alphonse); } 
     }).start(); 
    } 
} 

Hier Suns Erklärung:

Wenn Deadlock läuft, ist es extrem wahrscheinlich, dass beide Threads blockieren, wenn sie bowBack aufzurufen versuchen. Kein Block wird jemals enden, weil jeder Thread auf den anderen wartet, um Bogen zu verlassen.

Ich glaube nicht ganz zu folgen. Wenn alphonse.bow (Gaston) läuft, ist die Bogenmethode gesperrt. Jetzt wird es zuerst gedruckt "Gaston hat sich vor mir verbeugt!". Dann wird es weitergehen und BowBack rufen, und auch BowBack sperren. Wie kann dies zu einem Stillstand führen? Missverstehe ich, was passiert, wenn eine synchronisierte Methode aufgerufen wird?

Wenn mir jemand eine einfache Erklärung geben kann, danke.

+0

Sieht aus wie es viele richtige Antworten sind hier, aber ich denke, es könnte hilfreicher sein, die Antworten in zwei separate Spalten zu formatieren, von denen jede einen anderen Thread darstellt. Dies wird wirklich die Tatsache nach Hause fahren, dass 2 Sätze von Anweisungen gleichzeitig ausgeführt werden können. –

+1

mögliches Duplikat von [Versuch, mein kleines Gehirn umzustellen, wie Threads Deadlock] (http://stackoverflow.com/questions/749641/trying-to-wrap-my-wee-brain-around-how-threads-deadlock) –

+0

Ich habe ähnliche Verwirrung. Danke für den Beitrag und die Antworten. –

Antwort

27

Ein wichtiger Punkt besteht darin, festzustellen, dass es nicht Methoden ist, die gesperrt sind, aber Objektinstanzen.

Wenn Sie alphonse.bow(gaston) aufrufen, versucht es, die Sperre auf alphonse zu erwerben. Sobald es die Sperre hat, druckt es eine Nachricht und ruft dann gaston.bowBack(alphonse) auf. An diesem Punkt versucht es, die Sperre auf gaston zu erwerben. Sobald es die Sperre hat, druckt es eine Nachricht, gibt dann die Sperre frei, und schließlich wird die Sperre auf alphonse freigegeben.

Im Deadlock werden die Sperren in einer solchen Reihenfolge erfasst, dass für beide Threads keine Möglichkeit besteht, fortzufahren.

  • Gewinde 1: Sperre auf alphonse
  • Thread 2 erwirbt: erwirbt Sperre auf gaston
  • Thema 1: drucken Nachricht
  • Gewinde 1: versucht Sperre auf gaston zu erwerben - kann nicht, weil Thema 2 hat es schon.
  • Thread 2: druckt Nachricht
  • Thread 2: versucht, Sperre auf alphonse zu erhalten - kann nicht, weil Thread 1 bereits es hat.
+0

Ah. Ok, ich denke ich verstehe es. Wenn Sie also ein synchronisiertes Schlüsselwort vor einer Methode hinzufügen, bedeutet dies, dass das gesamte Objekt "sperrt", wenn ein Thread diese Methode aufruft, was bedeutet, dass kein anderer Thread eine andere Methode für dieses Objekt aufrufen kann, selbst wenn diese OTHER-Methoden einfach gedruckt werden "Hallo" ? – Saobi

+2

Nicht ganz. Kein Thread kann eine andere synchronisierte Methode für dieses Objekt aufrufen, und kein Thread kann in einen synchronisierten {} -Block eintreten, der für diese Objektinstanz synchronisiert wurde. –

+0

z.B. Andere Threads können alphonse.toString() oder alphonse.getName() aufrufen, auch wenn ein anderer Thread die Sperre für Alphonse besitzt. –

-3

Ich müsste überprüfen, aber ich denke, eine synchronisierte Methode sperrt das Klassenobjekt, also sperrt es andere synchronisierte Methoden in der gleichen Klasse.

Ich denke, es sperrt das Klassenobjekt selbst, so dass es sogar verschiedene Instanzen blockiert.

bearbeiten hinzuzufügen:

Werfen Sie einen Blick auf diesen Teil der java language spec

Jeder Bogen Methode seine eigene Objekte überwachen packt. Beide versuchen dann, den Bogen des anderen Objekts zurückzurufen und warten auf den anderen Monitor.

+4

Nein, synchronisiert in einer nicht statischen Methodensignatur sperrt nur die aktuelle Instanz, nicht das Klassenobjekt. –

+0

Den entsprechenden Teil der Spezifikation gefunden. Du hast recht. – developmentalinsanity

+0

Übrigens würde das Sperren des Klassenobjekts in diesem Fall tatsächlich den Deadlock verhindern! –

8

alphonse und gaston sind zwei verschiedene Objekte. Jedes Objekt verfügt über einen ihm zugeordneten inhärenten Monitor (Sperre).

Es könnte wie folgt geschehen:

alphonse erstellt wird. Sein Objektmonitor ist 1.

Gaston wird erstellt. Sein Objektmonitor ist 2.

alphonse.bow (gaston); Alphonse besitzt jetzt Schloss Nr. 1

gaston.Bogen (Alphonse); gaston besitzt nun Schloss # 2

alphonse ruft bowBack auf gaston und wartet auf Schloss # 2 gaston bowBack auf alphonse ruft und wartet auf Schloss # 1

Sinn? Verwenden der synchronisierten Schlüsselwortsperren, die Instanzen für die Dauer der Methode überwachen. Das Beispiel könnte wie folgt umgeschrieben werden:

+0

Wenn Alphonse bowBack aufruft, übergibt es seine eigene Instanz (bowBack (this)). Warum sollte bowBack Gastons Schloss benötigen? – Saobi

+0

Es ist nicht, es ist nur für die Protokollierung. Es hätte problemlos friend.getName() anstelle des gesamten Friend-Objekts übergeben können. – Kevin

1

Sperren werden auf Java-Objekten gehalten, nicht auf Java-Methoden. Wenn also eine Methode synchronisiert wird, wird das Objekt "this" gesperrt. Bei einer statischen Methode sperrt es das Klassenobjekt.

Sie können explizit den Monitor-Objekt angeben, indem synchronized (object) { }

0

synchronisiert in einer Methodendefinition ist eine Abkürzung für die Synchronisierung auf das Objekt (this) selbst. Im Wesentlichen bedeutet dies, dass bow() und bowBack() nicht gleichzeitig auf demselben Friend-Objekt aufgerufen werden können.

Wenn nun beide Freunde in den Bogen() kommen, kann keiner von beiden die bowBack() - Methode des anderen aufrufen.

1

Hinzufügen zu Simonn, et al.,

Wenn Sie diese sichtbar zu machen möchten, kompilieren und das Programm ausführen und einen Thread-Dump des laufenden Programms zu erzeugen. Sie können dies tun, indem Sie eine Ctrl - Break in eine Windows-Konsole eingeben oder einen kill -QUIT [pid] Befehl an ein * nix-System senden. Was dies Ihnen zur Verfügung stellt, ist eine Liste aller Threads, die in Ihrem System laufen und wo sie entweder laufen oder warten, sowie die Monitore, auf denen sich die Threads entweder befinden oder die darauf warten, gesperrt zu werden.

Wenn Sie Ihr Thema Namen in ihren Konstrukteuren ändern, werden Sie es leichter haben sie in der vollen Thread-Dump zu finden:

new Thread(new Runnable() { 
     public void run() { alphonse.bow(gaston); } 
    }, "Alphonse").start(); 
    new Thread(new Runnable() { 
     public void run() { gaston.bow(alphonse); } 
    }, "Gaston").start();