Que signifie ce code de jonction de thread?

156

Dans ce code, que signifient les deux jointures et cassures? t1.join()provoque l' t2arrêt jusqu'à la t1fin?

Thread t1 = new Thread(new EventThread("e1"));
t1.start();
Thread t2 = new Thread(new EventThread("e2"));
t2.start();
while (true) {
   try {
      t1.join();
      t2.join();
      break;
   } catch (InterruptedException e) {
      e.printStackTrace();
   }
}
user697911
la source
3
Puisqu'il va bloquer et attendre la fin du thread, pourquoi avez-vous utilisé la boucle while?
Mehdi
@MahdiElMasaoudi je suppose, pour continuer à attendre même si le fil est interrompu? Probablement pas une bonne façon de faire cela
forresthopkinsa
N'oubliez pas d'accepter une réponse ici si cela vous a été utile.
Gray
Comme mentionné, vous n'avez pas besoin while(true)d'appeler la joinméthode.
Chaklader Asfak Arefe

Réponses:

311

Que signifie ce code de jonction de thread?

Pour citer la Thread.join()méthode javadocs :

join() Attend que ce fil meure.

Il y a un thread qui exécute votre exemple de code qui est probablement le thread principal .

  1. Le thread principal crée et démarre les threads t1et t2. Les deux threads commencent à s'exécuter en parallèle.
  2. Le thread principal appelle t1.join()à attendre que le t1thread se termine.
  3. Le t1thread se termine et la t1.join()méthode retourne dans le thread principal. Notez que cela t1aurait déjà pu se terminer avant que l' join()appel ne soit effectué, auquel cas l' join()appel reviendra immédiatement.
  4. Le thread principal appelle t2.join()à attendre que le t2thread se termine.
  5. Le t2thread se termine (ou il s'est peut-être terminé avant le t1thread) et la t2.join()méthode retourne dans le thread principal.

Il est important de comprendre que les threads t1et t2ont été exécutés en parallèle, mais que le thread principal qui les a démarrés doit attendre qu'ils se terminent avant de pouvoir continuer. C'est un schéma courant. Aussi, t1et / ou t2aurait pu se terminer avant que le thread principal ne les appelle join(). Si c'est le cas, join()n'attendra pas mais reviendra immédiatement.

t1.join() signifie que t2 s'arrête jusqu'à ce que t1 se termine?

Non. Le thread principal qui appelle t1.join()s'arrêtera de s'exécuter et attendra la t1fin du thread. Le t2thread s'exécute en parallèle et n'est pas du tout affecté par t1l' t1.join()appel.

En termes de try / catch, les join()lancers InterruptedExceptionsignifient que le thread principal qui appelle join()peut lui-même être interrompu par un autre thread.

while (true) {

Avoir les jointures en whileboucle est un schéma étrange. En règle générale, vous effectuez la première jointure, puis la deuxième jointure en gérant InterruptedExceptioncorrectement dans chaque cas. Pas besoin de les mettre en boucle.

gris
la source
24
+1 C'est un schéma très étrange et peut probablement être supprimé.
m0skit0
3
Si t1 finit en premier, puis t2 pour finir. Cela semble être un processus séquentiel. Un fil se termine en premier, puis l'autre. quel est l'intérêt du filetage multiple?
user697911
9
Parce que t1et t2peut fonctionner en parallèle. C'est juste que mainles deux ont besoin de finir avant que cela puisse continuer. C'est un modèle typique @ user697911.
Gris
3
La whileboucle est-elle là parce que (je suppose) qu'elle veut réessayer les join()appels si l'un est interrompu? Je ne l'écrirais certainement pas de cette façon @ user697911.
Gris
5
La boucle est là pour assurer que les deux t1et t2finir. C'est à dire. si t1jette le InterruptedException, il bouclera et attendra t2. Une alternative consiste à attendre les deux threads dans chacun de leur Try-Catch, afin d'éviter la boucle. De plus, en fonction de EventThreadcela, il peut être judicieux de le faire de cette façon, car nous exécutons 2 threads, pas un.
Michael Bisbjerg
68

C'est une question d' entrevue Java préférée .

Thread t1 = new Thread(new EventThread("e1"));
t1.start();
Thread e2 = new Thread(new EventThread("e2"));
t2.start();

while (true) {
    try {
        t1.join(); // 1
        t2.join(); // 2  These lines (1,2) are in in public static void main
        break;
    }
}

t1.join()signifie, t1 dit quelque chose comme " Je veux terminer en premier ". Il en va de même avec t2. Peu importe qui a démarré t1ou t2thread (dans ce cas, la mainméthode), main attendra t1et t2finira sa tâche.

Cependant, un point important à noter, t1et t2eux - mêmes peuvent s'exécuter en parallèle indépendamment de la séquence d'appel de jointure sur t1et t2. C'est le main/daemonfil qui doit attendre .

AmitG
la source
3
Bon exemple. A propos de "peut fonctionner en parallèle": Et alors? Il est important que le thread principal attende EN PREMIER t1 et PUIS t2. Peu importe ce que font t1 ou t2 (du point de vue du thread principal)
Alex
1
Vous mentionnez le fil "main / daemon". Le principal je comprends mais le démon n'a rien à voir avec ça. Le thread principal n'est pas un démon.
Gray
1
t1.join(); t2.join();ne permettra pas au thread qui exécute les jointures de continuer tant que les deux threads ne sont pas terminés. En l'absence de code très inhabituel ailleurs, l'ordre des jointures n'a pas d'importance.
David Schwartz
Alors, t2.join () ne sera-t-il appelé que lorsque t1 sera terminé?
Leo Droidcoder
En d'autres termes, si nous voulons "sérialiser" l'exécution des threads t1 et t2, nous devons placer t1.join () juste après t1.start () puisque le thread principal a démarré t1 (et t2 par la suite) et bien sûr le supprimer de essayez / attrapez. Evidemment, ce faisant, la conséquence sera une perte de parallélisme.
dobrivoje
47

join()signifie attendre qu'un thread se termine. Ceci est une méthode de blocage. Votre thread principal (celui qui fait le join()) attendra sur la t1.join()ligne jusqu'à ce qu'il t1termine son travail, puis fera de même pour t2.join().

Avi
la source
29

Une image vaut mieux que mille mots.

    Main thread-->----->--->-->--block##########continue--->---->
                 \                 |               |
sub thread start()\                | join()        |
                   \               |               |
                    ---sub thread----->--->--->--finish    

J'espère utile, pour plus de détails cliquez ici

xxy
la source
3
Clair et précis.
dobrivoje
10

Lorsque le thread tA appelle tB.join (), il n'attend pas seulement que tB meure ou tA s'interrompe lui-même, mais crée une relation entre la dernière instruction de tB et l'instruction suivante après tB.join () dans le thread tA.

Toutes les actions d'un thread se produisent avant que tout autre thread ne retourne avec succès d'une jointure () sur ce thread.

Cela signifie programme

class App {
    // shared, not synchronized variable = bad practice
    static int sharedVar = 0;
    public static void main(String[] args) throws Exception {
        Thread threadB = new Thread(() -> {sharedVar = 1;});
        threadB.start();
        threadB.join();

        while (true) 
            System.out.print(sharedVar);
    }
}

Toujours imprimer

>> 1111111111111111111111111 ...

Mais programme

class App {
    // shared, not synchronized variable = bad practice
    static int sharedVar = 0;
    public static void main(String[] args) throws Exception {
        Thread threadB = new Thread(() -> {sharedVar = 1;});
        threadB.start();
        // threadB.join();  COMMENT JOIN

        while (true)
            System.out.print(sharedVar);
    }
}

Peut imprimer non seulement

>> 0000000000 ... 000000111111111111111111111111 ...

Mais

>> 00000000000000000000000000000000000000000000 ... 

Toujours seulement «0».

Parce que Java Memory Model ne nécessite pas de `` transfert '' de la nouvelle valeur de `` sharedVar '' de threadB vers le thread principal sans relation heppens-before (démarrage de thread, jointure de thread, utilisation du mot-clé `` synchonized '', utilisation des variables AtomicXXX, etc.).

Ivan Golovach
la source
5

En termes simples:
t1.join()retourne une fois t1terminé.
Il ne fait rien pour le filetage t1, sauf attendre la fin.
Naturellement, le code suivant t1.join()ne sera exécuté qu'après les t1.join()retours.

c0der
la source
1
ne sera exécuté qu'après le retour de t1.join () +1
mohsen.nour
3

Depuis la page de documentation d'Oracle sur les jointures

La joinméthode permet à un thread d'attendre la fin d'un autre.

Si t1 est un Threadobjet dont le thread est en cours d'exécution,

t1.join() : causes the current thread to pause execution until t1's thread terminates.

Si t2 est un Threadobjet dont le thread est en cours d'exécution,

t2.join(); causes the current thread to pause execution until t2's thread terminates.

joinL'API est une API de bas niveau, qui a été introduite dans les versions antérieures de java. Beaucoup de choses ont été modifiées au fil du temps (en particulier avec la version jdk 1.5) sur le front de la concurrence.

Vous pouvez réaliser la même chose avec l'API java.util.concurrent. Certains des exemples sont

  1. Utilisation de invokeAll surExecutorService
  2. En utilisant CountDownLatch
  3. Utilisation de ForkJoinPool ou newWorkStealingPool of Executors(depuis java 8)

Reportez-vous aux questions SE connexes:

attendez que tous les threads aient terminé leur travail en java

Ravindra babu
la source
1

Pour moi, le comportement de Join () était toujours déroutant car j'essayais de me rappeler qui attendra qui. N'essayez pas de vous en souvenir de cette façon.

En dessous, c'est un pur mécanisme wait () et notify ().

Nous savons tous que, lorsque nous appelons wait () sur n'importe quel objet (t1), l'objet appelant (main) est envoyé dans la salle d'attente (état bloqué).

Ici, le thread principal appelle join () qui est wait () sous les couvertures. Ainsi, le thread principal attendra d'être notifié. La notification est donnée par t1 à la fin de son exécution (achèvement du thread).

Après avoir reçu la notification, main sort de la salle d'attente et procède à son exécution.

Arjun Patil
la source
0

J'espère que ça aide!

package join;

public class ThreadJoinApp {

    Thread th = new Thread("Thread 1") {
        public void run() {
            System.out.println("Current thread execution - " + Thread.currentThread().getName());
            for (int i = 0; i < 10; i++) {
                System.out.println("Current thread execution - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    Thread th2 = new Thread("Thread 2") {
        public void run() {
            System.out.println("Current thread execution - " + Thread.currentThread().getName());

            //Thread 2 waits until the thread 1 successfully completes.
            try {
            th.join();
            } catch( InterruptedException ex) {
                System.out.println("Exception has been caught");
            }

            for (int i = 0; i < 10; i++) {
                System.out.println("Current thread execution - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    public static void main(String[] args) {
        ThreadJoinApp threadJoinApp = new ThreadJoinApp();
        threadJoinApp.th.start();
        threadJoinApp.th2.start();
    }

    //Happy coding -- Parthasarathy S
}
Parthasarathy S
la source
-3

disons que notre thread principal démarre les threads t1 et t2. Maintenant, quand t1.join () est appelé, le thread principal se suspend jusqu'à ce que le thread t1 meurt, puis se reprend. De même, lorsque t2.join () s'exécute, le thread principal se suspend à nouveau jusqu'à ce que le thread t2 meure, puis reprend.

Alors, voici comment cela fonctionne.

De plus, la boucle while n'était pas vraiment nécessaire ici.

skmangalam
la source