web-dev-qa-db-fra.com

Concept multithreading Java et méthode join ()

Je suis confus dans la méthode join() utilisée dans les threads en Java. Dans le code suivant:

// Using join() to wait for threads to finish.
class NewThread implements Runnable {

    String name; // name of thread
    Thread t;

    NewThread(String threadname) {
        name = threadname;
        t = new Thread(this, name);
        System.out.println("New thread: " + t);
        t.start(); // Start the thread
    }
// This is the entry point for thread.

    public void run() {
        try {
            for (int i = 5; i > 0; i--) {
                System.out.println(name + ": " + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            System.out.println(name + " interrupted.");
        }
        System.out.println(name + " exiting.");
    }
}

class DemoJoin {

    public static void main(String args[]) {
        NewThread ob1 = new NewThread("One");
        NewThread ob2 = new NewThread("Two");
        NewThread ob3 = new NewThread("Three");
        System.out.println("Thread One is alive: "
                + ob1.t.isAlive());
        System.out.println("Thread Two is alive: "
                + ob2.t.isAlive());
        System.out.println("Thread Three is alive: "
                + ob3.t.isAlive());
// wait for threads to finish
        try {
            System.out.println("Waiting for threads to finish.");
            ob1.t.join();
            ob2.t.join();
            ob3.t.join();
        } catch (InterruptedException e) {
            System.out.println("Main thread Interrupted");
        }
        System.out.println("Thread One is alive: "
                + ob1.t.isAlive());
        System.out.println("Thread Two is alive: "
                + ob2.t.isAlive());
        System.out.println("Thread Three is alive: "
                + ob3.t.isAlive());
        System.out.println("Main thread exiting.");
    }
}

Un exemple de sortie de ce programme est montré ici:

New thread: Thread[One,5,main]
New thread: Thread[Two,5,main]
New thread: Thread[Three,5,main]
Thread One is alive: true
Thread Two is alive: true
Thread Three is alive: true
Waiting for threads to finish.
One: 5
Two: 5
Three: 5
One: 4
Two: 4
Three: 4
One: 3
Two: 3
Three: 3
One: 2
Two: 2
Three: 2
One: 1
Two: 1
Three: 1
Two exiting.
Three exiting.
One exiting.
Thread One is alive: false
Thread Two is alive: false
Thread Three is alive: false
Main thread Exiting

Dans le code ci-dessus:

  1. Je ne suis pas en mesure de comprendre le déroulement de l'exécution du programme. Et lorsque ob1 est créé, le constructeur est appelé, où t.start() est écrit mais la méthode run() n'est pas encore exécutée, mais la méthode main() continue son exécution. Pourquoi cela se produit-il donc?

  2. La méthode join() est utilisée pour attendre que le thread sur lequel elle s'appelle ne se termine pas, mais ici, en sortie, nous voyons des sorties alternatives du thread, pourquoi ??

Et si l'utilisation de join est la suivante, quelle est l'utilisation de synchronized ??

Je sais qu'il me manque un concept de base, mais je ne suis pas en mesure de le comprendre, alors aidez-moi.

54
user2696258

Vous devez comprendre, la planification des threads est contrôlée par le planificateur de threads. Par conséquent, vous ne pouvez pas garantir l'ordre d'exécution des threads dans des circonstances normales.

Cependant, vous pouvez utiliser join() pour attendre qu'un thread termine son travail.

Par exemple, dans votre cas

ob1.t.join();

Cette instruction ne sera renvoyée que lorsque le thread t aura fini de s'exécuter.

Essaye ça,

class Demo {
   Thread t = new Thread(
                 new Runnable() {
                     public void run () {
                         //do something
                     }
                  }
    );
    Thread t1 = new Thread(
                 new Runnable() {
                     public void run () {
                         //do something
                     }
                  }
    );
    t.start(); // Line 15
    t.join();  // Line 16
    t1.start();
}

Dans l'exemple ci-dessus, votre thread principal est en cours d'exécution. Lorsqu'il rencontre la ligne 15, le thread t est disponible dans le planificateur de thread. Dès que le thread principal arrive à la ligne 16, il attendra que le thread t soit terminé.

NOTEZ que t.join n'a rien fait pour threader t ou pour threader t1. Cela n'affectait que le thread qui l'appelait (c'est-à-dire le thread main()).

Édité:

t.join(); doit être à l'intérieur du bloc try car il s'agit de throws l'exception InterruptedException, sinon vous obtiendrez une erreur lors de la compilation. Donc, ça devrait être:

try{
    t.join();
}catch(InterruptedException e){
    // ...
}
151
Malwaregeek

Tout d'abord, lorsque vous créez ob1, le constructeur est appelé et il commence l'exécution. À ce moment, t.start() est également exécuté dans un thread séparé. Rappelez-vous que lorsqu'un nouveau thread est créé, il est exécuté parallèlement au thread principal. Et c’est pourquoi main lance l’exécution à nouveau avec la prochaine instruction. 

Et l'instruction Join() est utilisée pour empêcher le fil enfant de devenir orphelin. Cela signifie que si vous n'avez pas appelé join() dans votre classe principale, le thread principal se fermera après son exécution et le thread enfant exécutera toujours les instructions. Join() attendra que tous les threads enfants aient terminé son exécution, puis seule la méthode principale se terminera.

Passez par this article, aide beaucoup.

4
Vimal Bera

Je ne suis pas en mesure de comprendre le déroulement de l'exécution du programme. Et quand ob1 est créé, le constructeur est appelé. T.start () est écrit mais la méthode run () n'est pas encore exécutée. La méthode main () continue l'exécution. Pourquoi cela se produit-il donc?

Cela dépend du planificateur de threads, car main partage le même ordre de priorité. L'appel de start () ne signifie pas que run () sera appelé immédiatement, cela dépend du planificateur de threads lorsqu'il choisit d'exécuter votre thread. 

la méthode join () est utilisée pour attendre que le thread sur lequel elle est appelée ne se termine pas, mais ici, en sortie, nous voyons des sorties alternatives du thread, pourquoi ??

Cela est dû au Thread.sleep (1000) dans votre code. Supprimez cette ligne et vous verrez les finitions ob1 avant ob2 qui se termine avant ob3 (comme prévu avec join ()). Cela dit, tout dépend du moment où ob1 ob2 et ob3 ont commencé. L'appel en veille suspendra l'exécution du thread pendant> = 1 seconde (dans votre code), ce qui permettra au planificateur d'appeler d'autres threads en attente (même priorité).

3
rmishra

Première règle du filetage - "Le filetage est amusant" ...

Je ne suis pas capable de comprendre le déroulement de l'exécution du programme, Et Lorsque ob1 est créé, le constructeur est appelé où t.start() est écrit mais la méthode run() n'est toujours pas exécutée, mais plutôt la méthode main() continue l'exécution. Pourquoi cela se produit-il donc?

C'est exactement ce qui devrait arriver. Lorsque vous appelez Thread#start, le thread est créé et son exécution est planifiée, cela peut arriver immédiatement (ou assez près de cela), mais pas forcément. Cela revient au planificateur de threads.

Cela dépend de la façon dont l'exécution du thread est planifiée et de ce qui se passe dans le système. En règle générale, chaque thread aura un peu de temps à exécuter avant de se remettre en veille et un autre thread est autorisé à s'exécuter (évidemment dans plusieurs environnements de processeur, plusieurs threads peuvent être exécutés à la fois, mais essayons et rester simple;))

Les threads peuvent également exécuter une exécution yield, permettant ainsi à d'autres threads du système de s'exécuter.

Tu pourrais essayer

NewThread(String threadname) {
    name = threadname;
    t = new Thread(this, name);
    System.out.println("New thread: " + t);
    t.start(); // Start the thread
    // Yield here
    Thread.yield();
}

Et cela pourrait faire une différence dans la façon dont les threads fonctionnent ... de la même manière, vous pourriez sleep pendant un court laps de temps, mais cela pourrait faire en sorte que votre thread soit ignoré pour une exécution de cycles ( vous ne).

La méthode join() est utilisée pour attendre le thread sur lequel elle s'appelle ne se termine pas, mais ici en sortie nous voyons des sorties alternatives de le fil pourquoi ??

La façon dont vous avez dit que la question est fausse ...join attendra la variable Thread à laquelle il est appelé à mourir avant de revenir. Par exemple, si vous dépendez du résultat d'une Thread, vous pouvez utiliser join pour savoir quand la Thread est terminée avant d'essayer d'extraire le résultat.

De même, vous pouvez interroger le thread, mais cela consommera des cycles de processeur qui pourraient être mieux utilisés par la variable Thread à la place ...

1
MadProgrammer

Je suis tombé sur la jointure () tout en me renseignant sur l'état de la course et je vais dissiper les doutes que j'avais. Prenons donc ce petit exemple 

Thread t2 = new Thread(
             new Runnable() {
                 public void run () {
                     //do something
                 }
              }
);
Thread t1 = new Thread(
             new Runnable() {
                 public void run () {
                     //do something
                 }
              }
);
t2.start(); //Line 11
t1.start(); //Line 12
t2.join();  //Line 13
t1.join();  //Line 14
System.out.print("<Want to print something that was being modified by t2 and t1>")

Mon AIM
Trois threads sont en cours d’exécution, à savoir t1, t2 et le thread principal. Je veux imprimer quelque chose une fois les t1 et t2 terminés. L’opération d’impression se fait sur mon fil principal; pour la réponse attendue, je dois donc laisser t1 et t2 finir puis imprimer ma sortie.

Donc, t1.join () fait simplement attendre le thread principal, jusqu'à ce que le thread t1 se termine avant de passer à la ligne suivante du programme. 

Voici la définition selon GeeksforGeeks :

La classe Java.lang.Thread fournit la méthode join () qui en autorise une thread attend jusqu'à ce qu'un autre thread termine son exécution.

Voici une question qui pourrait résoudre votre doute

Q-> Le thread t1 obtiendra-t-il la tranche de temps à exécuter par le planificateur de threads, lorsque le programme traitera t2.join () sur Line 13?

ANS-> Oui, il sera éligible pour obtenir la tranche de temps à exécuter car nous l'avons déjà rendu éligible en exécutant la ligne t1.start () à l'adresse Line 11
t2.join () applique uniquement la condition à laquelle la JVM ira à la ligne suivante, c'est la ligne 14.
Il se pourrait aussi que t1 le traitement est peut-être terminé à la ligne 13.

1
royatirek

La JVM et le système d'exploitation sous-jacent ont une liberté considérable lors de la planification d'activités. Le fait que vous obteniez jusqu'à "Attendre la fin des threads" avant de voir la sortie de chaque thread peut simplement signifier que le démarrage du thread prend un peu plus de temps (c'est-à-dire que cela prend un certain temps entre "en vie" et lorsque la méthode run () commence réellement à s'exécuter). Vous pourriez éventuellement voir la sortie du fil plus tôt, mais ce n'est pas garanti de toute façon.

Quant à join () , il ne garantit que ce qui se passera après ne se produira que lorsque le thread que vous rejoignez est terminé. Ainsi, lorsque vous avez trois join () appels consécutifs, cela ne signifie pas que les threads doivent se terminer dans un ordre particulier. Cela signifie simplement que vous allez attendre ob1 premier. Une fois que ob1 est terminé, ob2 et ob3 peut être encore en cours d’exécution ou peut-être déjà terminé. S'ils sont terminés, vos autres appels join () reviendront immédiatement.

synchronized est utilisé spécifiquement lorsque plusieurs threads accèdent au même objet et le modifient. Il est garanti qu'un bloc synchronisé ne sera jamais exécuté par deux threads simultanément - c'est-à-dire que le thread qui l'exécute a l'objet synchronisé tout seul.

1
codingjourney

quand ob1 est créé, le constructeur est appelé, où "t.start ()" est écrit mais la méthode run () n'est toujours pas exécutée, mais la méthode main () est exécutée plus loin. Pourquoi cela se produit-il donc?

ici, vos threads et votre thread principal ont la même priorité.L'exécution d'un thread de priorité égale dépend totalement du Thread schedular.Vous ne pouvez pas vous attendre à ce qu'il soit exécuté en premier.

la méthode join () est utilisée pour attendre que le thread sur lequel elle est appelée ne se termine pas, mais ici, en sortie, nous voyons des sorties alternatives du thread, pourquoi ??

Voici votre appel ci-dessous des déclarations du fil principal. 

     ob1.t.join();
     ob2.t.join();
     ob3.t.join();

Le thread principal attend donc que ob1.t, ob2.t, ob3.t meurt (regardez dans Thread # join doc ). Ainsi, les trois threads s'exécutent correctement et le thread principal se termine après celui-ci.

1
Prabhaker

Le planificateur de threads est responsable de la planification des threads. Donc, chaque fois que vous exécutez le programme, il n’ya aucune garantie quant à l’ordre d’exécution des threads. Supposons que vous ayez un objet thread nommé threadOne et si join () est appelé sur threadOne comme ceci:

threadOne.join ()

alors tous les threads en cours d'exécution seront suspendus jusqu'à ce que thread1 ait terminé son exécution ou se termine. 

Considérons le morceau de code suivant:

class RunnableSample implements Runnable {
    private Thread t;
    private String threadName;

    public RunnableSample(String name) {
        this.threadName = name;
    }
    public void run() {
        try {
            for(int i = 4; i >= 1; i--) {
                System.out.println(Thread.currentThread().getName() + ", " + i);
            Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            System.out.println(threadName + " interrupted");
        }
    }
    public void start() {
        if(t == null)
            t = new Thread(this, threadName);
        t.start();
        try {
            t.join();
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}
public class RunnableDemo {
    public static void main(String[] args) {
        RunnableSample r1 = new RunnableSample("threadOne");
        r1.start();

        RunnableSample r2 = new RunnableSample("threadTwo");
        r2.start();

        RunnableSample r3 = new RunnableSample("threadThree");
        r3.start();
     }
}

Le résultat du programme ci-dessus sera:

threadOne, 4
threadOne, 3
threadOne, 2
threadOne, 1
threadTwo, 4
threadTwo, 3
threadTwo, 2
threadTwo, 1
threadThree, 4
threadThree, 3
threadThree, 2
threadThree, 1

Comme join () est d'abord appelé sur threadOne, threadTwo et threadThree seront suspendus jusqu'à la fin de threadOne. (Notez que threadOne, threadTwo et ThreadThree ont tous commencé). Maintenant, les threads s'exécutent dans un ordre spécifique. Si join () n'est pas appelé sur un thread dans notre exemple, il n'y aura pas d'ordre d'exécution des threads.

public void start() {
    if(t == null)
        t = new Thread(this, threadName);
    t.start();
}

Sa sortie sera:

threadOne, 4
threadThree, 4
threadTwo, 4
threadTwo, 3
threadThree, 3
threadOne, 3
threadOne, 2
threadThree, 2
threadTwo, 2
threadOne, 1
threadThree, 1
threadTwo, 1

Venir à la synchronisation, ce qui est utile si vous souhaitez contrôler l’accès de plusieurs threads sur n’importe quelle ressource partagée. Si vous souhaitez limiter l'accès à des ressources partagées à un seul thread, la synchronisation est le meilleur moyen de le faire. 

1
Saathvik

Pas de mots, seulement du code courant 

// Thread class
public class MyThread extends Thread {

    String result = null;

    public MyThread(String name) {
        super(name);
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {

            System.out.println("Hello from " + this.getName());
        }
        result = "Bye from " + this.getName();
    }
}

Classe principale

public class JoinRND {
    public static void main(String[] args) {

        System.out.println("Show time");
        // Creating threads
        MyThread m1 = new MyThread("Thread M1");
        MyThread m2 = new MyThread("Thread M2");
        MyThread m3 = new MyThread("Thread M3");

        // Starting out Threads
        m1.start();
        m2.start();
        m3.start();
        // Just checking current value of thread class variable
        System.out.println("M1 before: " + m1.result);
        System.out.println("M2 before: " + m2.result);
        System.out.println("M3 before: " + m3.result);
        // After starting all threads main is performing its own logic in
        // parallel to other threads
        for (int i = 0; i < 1000; i++) {

            System.out.println("Hello from Main");
        }

        try {

            System.out
                    .println("Main is waiting for other threads to get there task completed");
            m1.join();
            m2.join();
            m3.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("M1 after" + m1.result);
        System.out.println("M2 after" + m2.result);
        System.out.println("M3 after" + m3.result);

        System.out.println("Show over");
    }
}
1
Vijay Tyagi

Mes commentaires:

Lorsque je vois la sortie, celle-ci est mélangée avec Un, Deux, Trois, qui sont les noms de thread et s'exécutent simultanément. Je ne suis pas sûr quand vous dites que le fil ne fonctionne pas par la méthode principale.

Je ne sais pas si j'ai compris votre question ou non. Mais je mets ma réponse ce que je pouvais comprendre, en espérant que cela puisse vous aider.

1) Ensuite, vous avez créé l’objet, il a appelé le constructeur. Dans sa construction, il a une méthode de démarrage qui a démarré le thread et exécuté le contenu écrit dans la méthode run (). 

Donc, comme vous avez créé 3 objets (3 threads - un, deux, trois), les 3 threads ont commencé à s'exécuter simultanément.

2) Jointure et synchronisation Il s’agit de deux choses différentes. La synchronisation se produit lorsque plusieurs threads partagent une même ressource et qu’un seul thread doit utiliser cette ressource à la fois. Par exemple. Des threads tels que DepositThread, WithdrawThread, etc. partagent un objet commun, BankObject. Ainsi, pendant que DepositThread est en cours d'exécution, le WithdrawThread attendra s'il est synchronisé. wait (), notify (), notifyAll () sont utilisés pour la communication inter-thread. Plz google pour en savoir plus.

à propos de Join (), c’est lorsque plusieurs threads sont en cours d’exécution, mais que vous rejoignez. par exemple. s'il y a deux threads t1 et t2 et qu'ils s'exécutent en multi-thread env, le résultat serait:. -2 T2-2

et nous utilisons t1.join (), ce serait:

Ceci est utilisé en temps réel lorsque, parfois, vous ne confondez pas le fil dans certaines conditions et que l'une dépend de l'autre à compléter (pas dans une ressource partagée), vous pouvez donc appeler la méthode join ().

1
user1460153

join () est une méthode d'instance de la classe Java.lang.Thread que nous pouvons utiliser avec la méthode join () pour garantir que tous les threads qui ont démarré à partir de main doivent se terminer dans l'ordre dans lequel ils ont démarré et où fin doit également se terminer. En d'autres termes, attend que ce fil meurt.

Exception: la méthode Join () lève une exception InterruptedException.

Etat du thread: Lorsque la méthode join () est appelée sur un thread, elle passe de l'état d'exécution à l'état d'attente. Et attendez que le fil meure.

bloc synchronisé: Le thread n'a pas besoin d'acquérir le verrou d'objet avant d'appeler la méthode join (), c'est-à-dire que la méthode join () peut être appelée depuis un bloc synchronisé externe.

Temps d'attente: Join (): Attend la fin de ce fil.

public final void join() throws InterruptedException;

Cette méthode appelle en interne join (0). Et timeout de 0 signifie attendre pour toujours;

join (long millis) - méthode synchronisée Attend, dans la plupart des millis millisecondes, la mort de ce fil. Un délai d'attente de 0 signifie attendre pour toujours.

public final synchronized void join(long millis)
    throws InterruptedException;

public final synchronized void join(long millis, int nanos)
    throws InterruptedException;

Exemple de méthode join

class MyThread implements Runnable {
     public void run() {
           String threadName = Thread.currentThread().getName();
           Printer.print("run() method of "+threadName);
           for(int i=0;i<4;i++){
                Printer.print("i="+i+" ,Thread="+threadName);
           }         
     }
}

public class TestJoin {
     public static void main(String...args) throws InterruptedException {
           Printer.print("start main()...");

           MyThread runnable = new MyThread();
           Thread thread1=new Thread(runnable);
           Thread thread2=new Thread(runnable);

           thread1.start();
           thread1.join();

           thread2.start();
           thread2.join();

           Printer.print("end main()");
     }
}

class Printer {
     public static void print(String str) {
           System.out.println(str);
     }
}

Output:
     start main()...
     run() method of Thread-0
     i=0 ,Thread=Thread-0
     i=1 ,Thread=Thread-0
     i=2 ,Thread=Thread-0
     i=3 ,Thread=Thread-0
     run() method of Thread-1
     i=0 ,Thread=Thread-1
     i=1 ,Thread=Thread-1
     i=2 ,Thread=Thread-1
     i=3 ,Thread=Thread-1
     end main()

Remarque: Appeler thread1.join () a fait en sorte que le thread principal attende que Thread-1 meure.

Vérifions un programme à utiliser pour joindre (long millis)

Tout d'abord, join (1000) sera appelé sur Thread-1, mais une fois que 1 000 millisecondes sont écoulées, le thread principal peut reprendre et démarrer thread2 (le thread principal n'attendra pas la fin de Thread-1).

class MyThread implements Runnable {
     public void run() {
           String threadName = Thread.currentThread().getName();
           Printer.print("run() method of "+threadName);
           for(int i=0;i<4;i++){
                try {
                     Thread.sleep(500);
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
                Printer.print("i="+i+" ,Thread="+threadName);
           }         
     }
}

public class TestJoin {
     public static void main(String...args) throws InterruptedException {
           Printer.print("start main()...");

           MyThread runnable = new MyThread();
           Thread thread1=new Thread(runnable);
           Thread thread2=new Thread(runnable);

           thread1.start();

           // once 1000 millisec are up,
           // main thread can resume and start thread2.
           thread1.join(1000);

           thread2.start();
           thread2.join();

           Printer.print("end main()");
     }
}

class Printer {
     public static void print(String str) {
           System.out.println(str);
     }
}

Output:
     start main()...
     run() method of Thread-0
     i=0 ,Thread=Thread-0
     run() method of Thread-1
     i=1 ,Thread=Thread-0
     i=2 ,Thread=Thread-0
     i=0 ,Thread=Thread-1
     i=1 ,Thread=Thread-1
     i=3 ,Thread=Thread-0
     i=2 ,Thread=Thread-1
     i=3 ,Thread=Thread-1
     end main()

Pour plus d'informations, voir mon blog:

http://javaexplorer03.blogspot.in/2016/05/join-method-in-Java.html

0
Rajesh Dixit

Voir le concept est très simple.

1) Tous les threads sont démarrés dans le constructeur et sont donc prêts à fonctionner. Main est déjà le fil d'exécution.

2) Maintenant, vous avez appelé le t1.join (). Ici, il se passe que le fil principal se noue derrière le fil t1. Vous pouvez donc imaginer un fil plus long avec le fil principal attaché à l'extrémité inférieure de t1.

3) Maintenant, il y a trois threads qui pourraient fonctionner: t2, t3 et thread combiné (t1 + main).

4) Maintenant, jusqu'à ce que t1 soit terminé, le principal ne peut pas fonctionner. l'exécution des deux autres instructions de jointure a donc été arrêtée.

5) Ainsi, le planificateur décide maintenant lequel des fils mentionnés ci-dessus (au point 3) est exécuté, ce qui explique le résultat.

0
ayush jain