web-dev-qa-db-fra.com

attendez que tous les threads finissent leur travail en java

J'écris une application qui a 5 threads qui obtiennent des informations de Web simultanément et remplissent 5 champs différents dans une classe de mémoire tampon.
Je dois valider les données du tampon et les stocker dans une base de données lorsque tous les threads ont terminé leur travail.
Comment puis-je faire cela (être alerté lorsque tous les threads ont terminé leur travail)? 

74
RYN

L’approche que j’utilise est d’utiliser un ExecutorService pour gérer des pools de threads.

ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<5;i++)
    es.execute(new Runnable() { /*  your task */ });
es.shutdown();
boolean finished = es.awaitTermination(1, TimeUnit.MINUTES);
// all tasks have finished or the time has been reached.
93
Peter Lawrey

Vous pouvez join aux discussions. La jointure est bloquée jusqu'à la fin du thread.

for (Thread thread : threads) {
    thread.join();
}

Notez que join jette une InterruptedException. Vous devrez décider quoi faire si cela se produit (par exemple, essayez d'annuler les autres threads pour éviter tout travail inutile).

42
Mark Byers

Regardez différentes solutions. 

  1. L'API join() a été introduite dans les premières versions de Java. Depuis la version 1.5 de JDK, ce package concurrent présente de bonnes alternatives.

  2. ExecutorService # invokeAll ()

    Exécute les tâches données en renvoyant une liste des contrats à terme contenant leur statut et leurs résultats lorsque tout est terminé.

    Reportez-vous à cette question SE associée pour un exemple de code:

    Comment utiliser invokeAll () pour laisser tout le pool de threads faire sa tâche?

  3. CountDownLatch

    Une aide à la synchronisation qui permet à un ou plusieurs threads d'attendre la fin d'un ensemble d'opérations effectuées dans d'autres threads.

    Un CountDownLatch est initialisé avec un nombre donné. Les méthodes en attente bloquent jusqu'à ce que le nombre actuel atteigne zéro en raison d'appels de la méthode countDown(), après quoi tous les threads en attente sont libérés et tous les appels ultérieurs de wait retournent immédiatement. C'est un phénomène ponctuel - le compte ne peut pas être réinitialisé. Si vous avez besoin d’une version qui réinitialise le nombre, envisagez l’utilisation de CyclicBarrier.

    Reportez-vous à cette question pour l'utilisation de CountDownLatch

    Comment attendre un fil qui engendre son propre fil?

  4. ForkJoinPool ou newWorkStealingPool () in Executors

  5. Itérer dans tout Futur objets créés après avoir été soumis à ExecutorService 

19
Ravindra babu

En plus de Thread.join() suggéré par d’autres, Java 5 a introduit le framework d’exécuteur. Là, vous ne travaillez pas avec des objets Thread. Au lieu de cela, vous soumettez vos objets Callable ou Runnable à un exécuteur. Il existe un exécuteur spécial conçu pour exécuter plusieurs tâches et renvoyer leurs résultats dans le désordre. C'est la ExecutorCompletionService :

ExecutorCompletionService executor;
for (..) {
    executor.submit(Executors.callable(yourRunnable));
}

Ensuite, vous pouvez appeler plusieurs fois take() jusqu'à ce qu'il ne reste plus d'objets Future<?> à renvoyer, ce qui signifie qu'ils sont tous terminés.


Une autre chose qui peut être pertinente, selon votre scénario, est CyclicBarrier .

Une aide à la synchronisation qui permet à un ensemble de threads de s’attendre mutuellement pour atteindre un point de barrière commun. CyclicBarriers sont utiles dans les programmes impliquant un groupe de threads de taille fixe qui doivent parfois s'attendre les uns les autres. La barrière est appelée cyclique car elle peut être réutilisée une fois les threads en attente libérés. 

10
Bozho

Une autre possibilité est l’objet CountDownLatch , qui est utile dans les situations simples: puisque vous connaissez à l’avance le nombre de threads, vous l’initialisez avec le nombre approprié et transmettez la référence de l’objet à chaque thread.
À la fin de sa tâche, chaque thread appelle CountDownLatch.countDown() qui décrémente le compteur interne. Le thread principal, après avoir démarré tous les autres, doit faire l'appel de blocage CountDownLatch.await(). Il sera libéré dès que le compteur interne aura atteint 0.

Faites attention avec cet objet, un InterruptedException peut également être lancé.

9
interDist

Tu fais

for (Thread t : new Thread[] { th1, th2, th3, th4, th5 })
    t.join()

Après cette boucle, vous pouvez être sûr que tous les threads ont terminé leur travail.

8
aioobe

Stockez les objets Thread dans une collection (telle qu'une liste ou un ensemble), puis parcourez la collection une fois les threads démarrés et appelez join () sur les threads.

4
esaj

Bien que cela ne soit pas pertinent pour OP, si vous êtes intéressé par la synchronisation (plus précisément, un rendez-vous) avec exactement un fil, vous pouvez utiliser un Echangeur

Dans mon cas, je devais mettre le thread parent en pause jusqu'à ce que le thread enfant fasse quelque chose, par exemple. terminé son initialisation. Un CountDownLatch fonctionne aussi bien.

2

Vous pouvez utiliser Thread # join method à cette fin.

2
Narendra Yadala

J'ai eu un problème similaire et fini par utiliser Java 8 parallelStream.

requestList.parallelStream().forEach(req -> makeRequest(req));

C’est super simple et lisible . En coulisse, il utilise le pool de jointure de fourches par défaut de la machine virtuelle Java, ce qui signifie qu’il attendra que tous les threads soient terminés avant de continuer. Pour mon cas, c’était une solution intéressante, car c’était le seul programme parallelStream de mon application. Si vous avez plusieurs parallélistes exécutés simultanément, veuillez lire le lien ci-dessous.

Plus d'informations sur les flux parallèles ici .

1
Madis Pukkonen

essayez ceci, fonctionnera.

  Thread[] threads = new Thread[10];

  List<Thread> allThreads = new ArrayList<Thread>();

  for(Thread thread : threads){

        if(null != thread){

              if(thread.isAlive()){

                    allThreads.add(thread);

              }

        }

  }

  while(!allThreads.isEmpty()){

        Iterator<Thread> ite = allThreads.iterator();

        while(ite.hasNext()){

              Thread thread = ite.next();

              if(!thread.isAlive()){

                   ite.remove();
              }

        }

   }
1
Jeyaraj.J

Attendez/bloquez le fil principal jusqu'à ce que d'autres threads terminent leur travail.

Comme @Ravindra babu l'a dit, cela peut être réalisé de différentes manières, mais en montrant des exemples.

  • Java.lang.Thread . join () Depuis: 1.0

    public static void joiningThreads() throws InterruptedException {
        Thread t1 = new Thread( new LatchTask(1, null), "T1" );
        Thread t2 = new Thread( new LatchTask(7, null), "T2" );
        Thread t3 = new Thread( new LatchTask(5, null), "T3" );
        Thread t4 = new Thread( new LatchTask(2, null), "T4" );
    
        // Start all the threads
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        // Wait till all threads completes
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    }
    
  • Java.util.concurrent.CountDownLatch Depuis: 1.5

    • .countDown() "Décrémente le compte du groupe de verrous.
    • .await() "Les méthodes en attente bloquent jusqu'à ce que le compte actuel atteigne zéro.

    Si vous avez créé latchGroupCount = 4, alors countDown() doit être appelé 4 fois pour que le décompte soit égal à 0. Ainsi, await() libérera les threads bloquants.

    public static void latchThreads() throws InterruptedException {
        int latchGroupCount = 4;
        CountDownLatch latch = new CountDownLatch(latchGroupCount);
        Thread t1 = new Thread( new LatchTask(1, latch), "T1" );
        Thread t2 = new Thread( new LatchTask(7, latch), "T2" );
        Thread t3 = new Thread( new LatchTask(5, latch), "T3" );
        Thread t4 = new Thread( new LatchTask(2, latch), "T4" );
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        //latch.countDown();
    
        latch.await(); // block until latchGroupCount is 0.
    }
    

Exemple de code de la classe Threaded LatchTask. Pour tester l'approche, utilisez joiningThreads(); et latchThreads(); de la méthode principale.

class LatchTask extends Thread {
    CountDownLatch latch;
    int iterations = 10;
    public LatchTask(int iterations, CountDownLatch latch) {
        this.iterations = iterations;
        this.latch = latch;
    }

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < iterations; i++) {
            System.out.println(threadName + " : " + i);
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task");
        // countDown() « Decrements the count of the latch group.
        if(latch != null)
            latch.countDown();
    }
}
  • CyclicBarriers Aide à la synchronisation qui permet à un ensemble de threads de s’attendre mutuellement pour atteindre un point de barrière commun. Les cyclicBarriers sont utiles dans les programmes impliquant une partie de threads de taille fixe qui doivent parfois s’attendre. La barrière est appelée cyclique car elle peut être réutilisée une fois les threads en attente libérés.
    CyclicBarrier barrier = new CyclicBarrier(3);
    barrier.await();
    
    Par exemple, référez-vous à cette classe Concurrent_ParallelNotifyies .

  • Cadre d'exécution: nous pouvons utiliser ExecutorService pour créer un pool de threads et suivre la progression des tâches asynchrones avec Future.

    • submit(Runnable), submit(Callable) qui renvoient un objet futur. En utilisant la fonction future.get(), nous pouvons bloquer le thread principal jusqu'à ce que le thread de travail termine son travail.

    • invokeAll(...) - renvoie une liste d'objets Future via lesquels vous pouvez obtenir les résultats des exécutions de chaque Callable.

Trouver un exemple d'utilisation du framework Interfaces Runnable, Callable with Executor.


@Voir également

1
Yash

Un service exécuteur peut être utilisé pour gérer plusieurs threads, y compris l'état et l'achèvement. Voir http://programmingexamples.wikidot.com/executorservice

1
user974465

J'ai créé une petite méthode d'assistance pour attendre quelques threads:

public static void waitForThreadsToFinish(Thread... threads) {
        try {
            for (Thread thread : threads) {
                thread.join();
            }
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
0
Alon Gouldman

Les réponses existantes dites pourraient join() chaque thread.

Mais il existe plusieurs façons d’obtenir le tableau/la liste des threads: 

  • Ajoutez le fil à une liste lors de la création.
  • Utilisez ThreadGroup pour gérer les threads.

Le code suivant utilisera l'approche ThreadGruop. Il crée d’abord un groupe, puis, lorsque chaque thread est créé, spécifie le groupe dans le constructeur, il peut ultérieurement obtenir le tableau de threads via ThreadGroup.enumerate().


Code

SyncBlockLearn.Java

import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * synchronized block - learn,
 *
 * @author eric
 * @date Apr 20, 2015 1:37:11 PM
 */
public class SyncBlockLearn {
    private static final int TD_COUNT = 5; // thread count
    private static final int ROUND_PER_THREAD = 100; // round for each thread,
    private static final long INC_DELAY = 10; // delay of each increase,

    // sync block test,
    @Test
    public void syncBlockTest() throws InterruptedException {
        Counter ct = new Counter();
        ThreadGroup tg = new ThreadGroup("runner");

        for (int i = 0; i < TD_COUNT; i++) {
            new Thread(tg, ct, "t-" + i).start();
        }

        Thread[] tArr = new Thread[TD_COUNT];
        tg.enumerate(tArr); // get threads,

        // wait all runner to finish,
        for (Thread t : tArr) {
            t.join();
        }

        System.out.printf("\nfinal count: %d\n", ct.getCount());
        Assert.assertEquals(ct.getCount(), TD_COUNT * ROUND_PER_THREAD);
    }

    static class Counter implements Runnable {
        private final Object lkOn = new Object(); // the object to lock on,
        private int count = 0;

        @Override
        public void run() {
            System.out.printf("[%s] begin\n", Thread.currentThread().getName());

            for (int i = 0; i < ROUND_PER_THREAD; i++) {
                synchronized (lkOn) {
                    System.out.printf("[%s] [%d] inc to: %d\n", Thread.currentThread().getName(), i, ++count);
                }
                try {
                    Thread.sleep(INC_DELAY); // wait a while,
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.printf("[%s] end\n", Thread.currentThread().getName());
        }

        public int getCount() {
            return count;
        }
    }
}

Le thread principal attendra que tous les threads du groupe soient terminés.

0
Eric Wang