2009-10-13 12 views
11

J'apprends en Java sur les blocages, et il y a cet exemple de code de tutoriel officiel de Sun:Question A propos Deadlock Situation en Java

Alphonse et Gaston sont des amis, et grands croyants dans la courtoisie. Une règle stricte de courtoisie est que lorsque vous inclinez à un ami, vous devez rester incliné jusqu'à ce que votre ami a une chance de retourner l'arc. Malheureusement, cette règle ne tient pas compte de la possibilité que deux amis puissent s'incliner l'un à l'autre en même temps.

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(); 
    } 
} 

est ici l'explication de Sun:

Lorsque Deadlock fonctionne, il est extrêmement probable que les deux fils bloquent lorsqu'ils tentent d'invoquer bowBack. Aucun bloc ne se terminera jamais, car chaque thread attend l'autre pour quitter l'arc.

Je ne semble pas tout à fait suivre. Quand alphonse.bow (gaston) s'exécute, la méthode d'arc est verrouillée. Alors maintenant, il va d'abord imprimer "Gaston s'est incliné devant moi!". Ensuite, il va continuer à appeler bowBack, et verrouille bowBack aussi bien. Comment cela peut-il causer une impasse? Ai-je mal compris ce qui se passe quand une méthode synchronisée est appelée?

Si quelqu'un peut me donner une explication facile, merci.

+0

On dirait qu'il ya beaucoup de bonnes réponses, mais je pense qu'il pourrait être plus utile pour formater les réponses en 2 colonnes distinctes, chacune représentant un fil différent. Cela va vraiment motiver le fait que 2 séries d'instructions peuvent être exécutées simultanément. –

+1

duplicata possible de [Essayer d'envelopper mon cerveau wee autour de l'impasse des threads] (http://stackoverflow.com/questions/749641/trying-to-wrap-my-wee-brain-around-how-threads-deadlock) –

+0

J'ai une confusion similaire. Merci pour le message et les réponses. –

Répondre

27

Un point important à noter est que ce n'est pas méthodes qui sont verrouillés, mais instances d'objet.

Lorsque vous appelez alphonse.bow(gaston), il tente d'acquérir le verrou sur alphonse. Une fois qu'il a le verrou, il imprime un message, puis appelle gaston.bowBack(alphonse). À ce stade, il tente d'acquérir le verrou sur gaston. Une fois qu'il a le verrou, il imprime un message, puis libère le verrou, et enfin le verrou alphonse est libéré.

Dans un interblocage, les verrous sont acquis dans un ordre tel qu'il n'y ait aucun moyen pour l'un ou l'autre thread de continuer.

  • Discussion 1: acquiert verrou sur alphonse
  • Discussion 2: acquiert verrou sur gaston
  • Discussion 1: imprime un message
  • Discussion 1: tente d'acquérir un verrou sur gaston - ne peut pas, parce que cette discussion 2 l'a déjà. Thread 2: affiche le message
  • Thread 2: tente d'acquérir le verrou alphonse - impossible, car le thread 1 l'a déjà.
+0

Ah. Ok, je pense que je comprends. Ainsi, lorsque vous ajoutez un mot-clé synchronisé devant une méthode, cela signifie que lorsqu'un thread appelle cette méthode, l'objet entier "verrouille", ce qui signifie qu'aucun autre thread ne peut appeler une autre méthode sur cet objet, même si ces méthodes "Bonjour" ? – Saobi

+2

Pas tout à fait. Aucun thread ne peut appeler une autre méthode synchronisée sur cet objet, et aucun thread ne peut entrer un bloc synchronisé {} synchronisé sur cette instance d'objet. –

+0

par exemple. les autres threads peuvent appeler alphonse.toString() ou alphonse.getName() même si un thread différent possède le verrou sur alphonse. –

-3

Je devrais vérifier, mais je pense qu'une méthode synchronisée se verrouille sur l'objet de classe, donc il verrouille les autres méthodes synchronisées dans la même classe. Je pense qu'il verrouille sur l'objet de classe lui-même, de sorte qu'il bloque même différentes instances.

Modifier pour ajouter:

Jetez un oeil à cette partie du java language spec

Chaque méthode arc saisit son propre moniteur des objets. Les deux essayent ensuite d'appeler l'arc de l'autre objet et de bloquer l'attente de l'autre moniteur.

+4

Non, synchronisé dans une signature de méthode non statique verrouillera uniquement l'instance actuelle, pas l'objet classe. –

+0

Trouvé la partie appropriée de la spécification. Tu as raison. – developmentalinsanity

+0

Incidemment, le verrouillage de l'objet de classe empêcherait le blocage dans ce cas! –

8

alphonse et gaston sont deux objets différents. Chaque objet possède un moniteur intrinsèque (verrou) qui lui est associé.

Il pourrait se produire comme ceci:

alphonse est créé. Son moniteur d'objet est 1.

gaston est créé. Son moniteur d'objet est 2.

alphonse.bow (gaston); alphonse possède maintenant le verrou # 1

gaston.arc (alphonse); gaston possède maintenant verrou # 2

appelle alphonse bowBack sur gaston et attend serrure # 2 gaston appelle bowBack sur alphonse et attend serrure # 1

de sens? L'utilisation du mot clé synchronized verrouille les instances qui surveillent la durée de la méthode. L'exemple a pu être réécrit comme suit:

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

Quand alphonse appelle bowBack, il passe sa propre instance (bowBack (this)). Pourquoi bowBack aurait-il besoin du verrou de gaston? – Saobi

+0

Ce n'est pas le cas, c'est juste pour la consignation. Il aurait pu facilement passer friend.getName() au lieu de l'objet Friend entier. – Kevin

1

Les verrous sont conservés sur les objets Java et non sur les méthodes java. Ainsi, lorsque synchronized est utilisé sur une méthode, il verrouille l'objet "this". Dans le cas d'une méthode statique, il verrouille l'objet de classe.

Vous pouvez spécifier explicitement l'objet de moniteur à l'aide synchronized (object) { }

0

synchronisé dans une définition de méthode est un raccourci pour synchroniser sur l'objet (this) lui-même. En substance, cela signifie que bow() et bowBack() ne peuvent pas être invoqués mutuellement sur le même objet Friend. Maintenant, si les deux amis entrent dans bow(), aucun d'eux ne pourra invoquer la méthode bowBack() de l'autre.

1

Pour ajouter à Simonn, et al.,

Si vous souhaitez visualiser cela, compiler et exécuter le programme et générer un vidage de fil du programme en cours d'exécution. Vous pouvez le faire en tapant Ctrl - Break sur une console Windows ou en envoyant une commande kill -QUIT [pid] à un système * nix. Ce que cela va vous fournir est une liste de tous les Threads en cours d'exécution dans votre système et où ils sont en cours d'exécution ou en attente, ainsi que les moniteurs que les threads verrouillent ou attendent de verrouiller.

Si vous changez vos noms de discussion dans leurs constructeurs, vous aurez un temps plus facile de les trouver dans le plein décharge de fil:

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