2017-04-08 3 views
2

Comment puis-je appeler un thread particulier dans la communication inter-thread?Comment notifier un thread spécifique en Java

Dans le programme ci-dessous, j'ai deux threads t1 et t2.

Quand j'appelle t1.notify() il soulève:

Exception in thread "Thread-1" java.lang.IllegalMonitorStateException 
    at java.lang.Object.notify(Native Method) 
    at Shared.methodTwo(NotifyThread.java:43) 
    at Thread2.run(NotifyThread.java:77) 
Error 

class Shared { 

    Thread1 t1 ; 
    Thread2 t2 ; 

    void ThreadInit(Thread1 t1 , Thread2 t2) { 
     this.t1 = t1 ; 
     this.t2 = t2 ; 
    } 

    synchronized void methodOne() 
    { 
     Thread t = Thread.currentThread(); 

     System.out.println(t.getName()+" is relasing the lock and going to wait"); 

     try 
     { 
      wait();  //releases the lock of this object and waits 
     } 
     catch (InterruptedException e) 
     { 
      e.printStackTrace(); 
     } 

     System.out.println(t.getName()+" got the object lock back and can continue with it's execution"); 
    } 

    synchronized void methodTwo() 
    { 
     Thread t = Thread.currentThread(); 

     try 
     { 
      Thread.sleep(5000); 
     } 
     catch (InterruptedException e) 
     { 
      e.printStackTrace(); 
     } 

     t1.notify();  

     System.out.println("A thread which is waiting for lock of this object is notified by "+t.getName()); 
    } 
    } 

    class Thread1 extends Thread 
    { 
    Shared s ; 
    Thread1(Shared s) { 

     this.s = s ; 
    } 

    public void run() 
      { 
       s.methodOne(); //t1 calling methodOne() of 's' object 
      } 

    } 

    class Thread2 extends Thread { 
     Shared s ; 
    Thread2(Shared s) { 

     this.s = s ; 

    } 

    public void run() 
      { 
       s.methodTwo(); //t1 calling methodOne() of 's' object 
      } 


    } 
    public class NotifyThread 
    { 
    public static void main(String[] args) 
    { 
     final Shared s = new Shared(); 

     Thread1 t1 = new Thread1(s) ; 
     Thread2 t2 = new Thread2(s) ; 

     s.ThreadInit(t1,t2) ; 

     t1.start(); 
     t2.start(); 
    } 
} 

Répondre

3

vous n'avisez pas un fil spécifique. Vous appelez notify sur un objet lock. Cela réveille un fil qui attend sur la serrure. (Dans votre cas, l'objet de verrouillage est un fil ... qui embrouille plutôt l'image. Cependant, voir ci-dessous.)

Mais votre problème (le IllegalMonitorStateException) se produit parce que le fil faisant la notification (à savoir le courant thread) ne tient pas le verrou. C'est une exigence (dure) que le thread actuel doit maintenir le verrou lorsqu'il notifie un verrou.

Pour plus de détails, lisez les javadocs pour Object.wait(timeout) ou (par exemple) ceci: http://howtodoinjava.com/core-java/multi-threading/how-to-work-with-wait-notify-and-notifyall-in-java/


Je ne voudrais pas utiliser un objet Thread comme un objet de verrouillage. Cela fonctionnera probablement, mais il y a aussi une chance que quelque chose d'autre (peut-être quelque chose dans le système d'exécution) verrouille/attende/notifie les objets Thread. Ensuite, les choses deviennent très confuses.

Il est MIEUX de créer des objets verrouillés spécifiquement à cet effet; par exemple.

private final Object lock = new Object(); 

De plus, l'écriture des classes qui étendent Thread est généralement une mauvaise idée. Il est généralement préférable d'implémenter l'interface Runnable, de l'instancier et de passer l'instance en tant que paramètre au constructeur Thread; par exemple.

Thread t = new Thread(new Runnable() { 
    public void run() { 
     System.out.println("Hello world"); 
    }}); 
t.start(); 

Un avantage de la mise en œuvre Runnable plutôt que d'étendre Thread est que vous pouvez utiliser votre code plus facilement avec quelque chose qui gère les cycles de vie de fil pour vous; par exemple. un ExecutorService, un pool de threads de jointure de fourche ou un pool de threads classique.

Une seconde est que la logique de thread léger peut être implémentée de façon concise comme une classe anonyme ... comme dans mon exemple.

+0

Mais vous ne notifiez pas le thread. Vous appelez notifier sur le verrou et il délègue la notification au planificateur d'OS, et le planificateur décide quel thread dans l'ensemble d'attente est notifié. Ce que je sais que vous savez, mais votre formulation choisie semble ne pas faire un point important que ce sujet est notifié n'est pas à la demande d'avis de thread. Ce qui peut être ce que le PO est confus. –

+0

Est-ce mieux? –

+0

Et tout ce qui est dit (ce qui est un conseil assez décent) je ne voudrais pas utiliser wait/notify du tout. J'utiliserais certaines classes du paquet 'java.util.concurrent':' Lock', 'Semaphore' et' CyclicBarrier' sont [tous de meilleurs paris] (http://winterbe.com/posts/2015/04/30/java8-concurrence-tutorial-synchronized-locks-examples /) plutôt que d'essayer de gérer vous-même la synchronisation. – markspace

1

Pour ajouter des points;

Votre code utilise des verrous intrinsèques. Chaque objet de la machine virtuelle Java possède son propre verrou. Ce verrou n'a rien à voir avec la fonctionnalité de l'objet. Acquérir le verrou par lui-même ne fait rien (en l'absence d'autres mesures comme l'utilisation du mot-clé synchronized) pour empêcher les autres threads de mijoter avec le contenu de l'objet. Appeler notify sur un thread ne signifie pas que ce thread recevra une notification. Comme mentionné précédemment, l'acquisition du verrou sur les objets Thread est déconseillée. La méthode de jointure sur Thread utilise le verrou intrinsèque sur le thread joint.Si le code acquiert un verrou pour différentes raisons, un thread peut être notifié pour une condition dont il ne tient pas compte.

Le verrou intrinsèque est un entrelacement qui indique au planificateur de système d'exploitation quels sont les threads en attente. Le planificateur de système d'exploitation décide quels threads dans l'ensemble d'attente pour le verrou sont notifiés. Quand un thread appelle notify sur un objet, il dit au verrou sur cet objet de dire au planificateur de choisir quel thread d'attente est averti. Le verrou sait quels threads sont en attente mais ne sait pas quelle condition ils attendent. (ReentrantLock est une grande amélioration pour cela, voir le document API pour Condition.)

Bien sûr, notifyAll réveille tous les threads dans l'attente, mais encore une fois c'est quelque chose que le verrou et le planificateur connaissent. Le thread appelant notifyAll ne sait pas quels threads attendent. Le système est conçu intentionnellement pour que les threads ne puissent pas notifier les autres threads directement. Encore une autre chose ici est que l'appel en attente sans vérification d'une condition n'est pas fiable. Si un thread n'a pas acquis de verrou avant qu'une notification ne soit faite, il rate cette notification. Si un thread reçoit une notification, cela ne garantit pas qu'il obtiendra le verrou suivant, un autre thread pourrait agir et invalider l'état attendu par le thread notifié. Toujours avoir un état interne que le thread actuel peut vérifier pour vérifier que l'état de l'objet correspond à ce que le thread attend, et faire de cette vérification le test dans une boucle. Par exemple, si j'ai une file d'attente bloquée de taille fixe (implémentée en utilisant une liste en interne, la protégeant de l'accès simultané en utilisant la synchronisation) où les threads tentent de retirer quelque chose de la file mais bloquent ressemblent:

public synchronized T take() throws InterruptedException { 
    while (list.isEmpty()) { 
     wait(); 
    } 
    notifyAll(); 
    return list.remove(0); 
} 

une fois un fil d'attente se réveille et acquiert à nouveau le verrou, il vérifie pour voir si la situation actuelle est ce qu'il a attendu. Seulement si c'est le cas, le thread doit quitter la boucle et continuer.