2009-12-17 6 views
3

Je viens de .NET monde, et malheureusement la source de Java avec les yeux de .NET.Jeux de synchronisation Java: synchronisation && wait && notifier

code suivant est d'Android Apps (mais pas Android spécifique du tout):

private class Worker implements Runnable { 
     private final Object mLock = new Object(); 
     private Looper mLooper; 

     Worker(String name) { 
      Thread t = new Thread(null, this, name); 
      t.start(); 
      synchronized (mLock) { 
       while (mLooper == null) { 
        try { 
         mLock.wait(); 
        } catch (InterruptedException ex) { 
        } 
       } 
      } 
     } 

     public Looper getLooper() { 
      return mLooper; 
     } 

     public void run() { 
      synchronized (mLock) { 
       Looper.prepare(); 
       mLooper = Looper.myLooper(); 
       mLock.notifyAll(); 
      } 
      Looper.loop(); 
     } 

     public void quit() { 
      mLooper.quit(); 
     } 
    } 

Je ne suis pas exactement clair avec la façon dont fonctionne synchronized. D'abord je pensais que synchronized verrouille l'objet mLock, mais si après thread t.start() constructeur entre d'abord le bloc de synchronisation, il le bloquerait à mLock.wait(), et bloquerait implicitement le fil "t" en le bloquant d'entrer le bloc synchronisé.

Ceci est évidemment faux, parce que mon téléphone sonne comme on le suppose :)

pensée suivante est que Synchronize synchronise « bloc de code » (dans ce cas, il deux blocs synchronisés sont des threads indépendants => peuvent entrer dans deux de synchronisation différents bloquer en même temps sans restriction), et cela correspond parfaitement ...

... jusqu'à ce que mon collègue me dise que les versions mLock.wait() se verrouillent sur mLock et permettent aux autres threads d'entrer dans la section critique sur mLock en même temps.

Je ne suis pas sûr d'avoir été assez clair, donc je répondrai volontiers à d'autres questions à ce sujet.

+0

Première question: quelle est votre question? – erickson

+0

Comment fonctionne ce code? :) – Jox

Répondre

8

Découvrez le javadoc sur Object.wait(). C'est "magique" en ce qu'il laisse tomber le moniteur qui a été acquis en entrant dans le bloc synchronisé {}. Cela permet à un autre thread d'acquérir le moniteur et d'appeler le Object.notify(). Quand un autre thread appelle notify() pour sortir le thread en attente de son appel wait(), le thread en attente doit ré-acquérir le moniteur et le bloquer jusqu'à ce qu'il le puisse - le moniteur est seulement abandonné pendant la durée du wait() appel. Et le thread notifiant termine son bloc synchronisé avant que le thread d'attente nouvellement réveillé puisse continuer. Tout est séquencé de manière prévisible.

+1

C'est en fait l'ingrédient clé pour comprendre la synchronisation en Java: 'wait()' * libère * le verrou. Tout est devenu clair dès que j'ai compris cela. – Bombe

+0

OK ... C'est plus clair maintenant :) Encore une question: Disons par exemple que dans le bloc de synchronisation de run(), il y a quelques lignes de plus après la ligne 'mLock.notifyAll()'. Si j'ai bien compris, le thread du constructeur ne poursuivra pas l'exécution de son code non pas immédiatement après notify() de notify(), mais seulement après que le thread de run() quitte le bloc de synchronisation? – Jox

+2

Comme mentionné dans le fichier notify() javadoc, l'appelant notify() ne libère pas le moniteur tant qu'il ne le ferait normalement (en quittant le bloc synchronisé ou autre). Ainsi, le thread wait() nouvellement réveillé est bloqué jusqu'à ce qu'il puisse ré-acquérir le moniteur. Cela ne peut pas arriver avant que l'appelant notify() ne le libère. Ainsi, le code suivant notify() avant que la fin du bloc synchronisé ne s'exécute définitivement avant que n'importe quel autre thread n'achemine le moniteur. –

1

synchronized utilise des moniteurs d'objets. L'appel wait() sur l'objet libère atomiquement le moniteur d'objet (sinon aucun autre thread ne pourrait jamais prendre le moniteur et émettre un notify au (x) serveur (s)).

1

Oui. Si vous lisez la description de la méthode wait(), vous apprendrez que le thread libère le verrou et bloque jusqu'à ce qu'un autre thread invoque notify ou notifyAll sur le verrou. Le thread en cours attend jusqu'à ce qu'il puisse ré-acquérir le verrou, et une fois qu'il l'a fait, il continue l'exécution. Toutefois, le code affiché est mal utilisé, car il "publie" (c'est-à-dire qu'il rend l'objet accessible à d'autres threads) l'instance Worker avant qu'il ne soit entièrement construit. L'utilisation de barrières supplémentaires dans cette méthode, combinée à la nature de la classe private, rend probablement ce cas sûr, mais en général, il ne l'est pas.

0

Permettez-moi d'expliquer:

Le constructeur lance un nouveau thread qui exécutera la méthode run(). Ce nouveau thread obtiendra un nouvel objet Looper, le stockera dans le champ mLooper, puis exécutera la boucle de message Looper. Entre les deux, notifiera() le premier thread que mLooper a été défini.

Le premier thread retournera donc du constructeur seulement après que mLooper a été défini, ce qui signifie que le traitement de Looper.loop(), par le 2ème thread, est lié pour démarrer bientôt/a déjà commencé.