attendre que tous les threads finissent leur travail en java

j'écris une application qui a 5 threads qui obtiennent des informations du web simultanément et remplissent 5 champs différents dans une classe buffer.

J'ai besoin de valider les données du tampon et de 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) ?

72
demandé sur Prince John Wesley 2011-10-29 17:40:24

13 réponses

l'approche que je prends est d'utiliser un ExecutorService pour gérer les pools de threads.

ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<5;i++)
    es.execute(new Runnable() { /*  your task */ });
es.shutdown();
boolean finshed = es.awaitTermination(1, TimeUnit.MINUTES);
// all tasks have finished or the time has been reached.
84
répondu Peter Lawrey 2016-06-26 10:04:09

vous pouvez join aux fils. La jointure bloque jusqu'à ce que le fil soit terminé.

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

notez que join lance un InterruptedException . Vous aurez à décider quoi faire si cela se produit (par exemple essayer d'annuler les autres fils pour éviter le travail inutile étant fait).

40
répondu Mark Byers 2016-07-23 06:35:32

regardez les différentes solutions.

  1. join() L'API a été introduite dans les premières versions de Java. Quelques bonnes alternatives sont disponibles avec ce paquet concurrent depuis la version JDK 1.5.

  2. ExecutorService#invokeAll ()

    exécute les tâches données, en retournant une liste des futurs ils conservent leur statut et leurs résultats lorsque tout est terminé.

    se Réfèrent à cette question SE pour exemple de code:

    comment utiliser invokeAll() pour laisser tous les thread pool faire leur tâche?

  3. CountDownLatch

    Une synchronisation de l'aide qui permet à un ou plusieurs threads d'attendre jusqu'à ce qu'un ensemble de les opérations effectuées dans d'autres threads complète.

    Un CountDownLatch est initialisé avec un comte. Les méthodes d'attente bloquent jusqu'à ce que le compte courant atteigne zéro à cause des invocations de la méthode countDown() , après quoi tous les threads d'attente sont libérés et toutes les invocations subséquentes d'attente reviennent immédiatement. Il s'agit d'un phénomène à un seul coup -- le compte ne peut pas être réinitialisé. Si vous avez besoin d'une version qui réinitialise le compte, considérez utilisant un CyclicBarrier .

    se référer à cette question pour l'usage de CountDownLatch

    comment attendre un fil qui le produit?

  4. ForkJoinPool ou newWorkStealingPool() dans Exécuteurs

  5. Itérer sur tous les Avenir les objets créés après la présentation de ExecutorService

18
répondu Ravindra babu 2017-11-03 17:08:21

mis à part Thread.join() suggéré par d'autres, java 5 a introduit le cadre exécuteur. Là, vous ne travaillez pas avec des objets Thread . Vous soumettez plutôt vos objets Callable ou Runnable à un exécuteur testamentaire. Il y a un exécuteur spécial qui est censé exécuter plusieurs tâches et retourner leurs résultats hors de l'ordre. C'est la ExecutorCompletionService :

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

alors vous pouvez à plusieurs reprises appeler take() jusqu'à ce qu'il n'y ait pas plus Future<?> objets à retourner, ce qui signifie que tous sont terminés.


une autre chose qui peut être pertinente, selon votre scénario: CyclicBarrier .

une aide à la synchronisation qui permet à un ensemble de threads d'attendre l'autre pour atteindre un point de barrière commune. CyclicBarriers sont utiles dans les programmes impliquant une partie de taille fixe de fils qui doit parfois attendre les uns des autres. La barrière est appelée cyclique parce qu'elle peut être réutilisée après que les fils d'attente sont libérés.

10
répondu Bozho 2016-06-22 16:07:01

une autre possibilité est l'objet CountDownLatch , qui est utile pour des situations simples : puisque vous connaissez à l'avance le nombre de threads, vous l'initialisez avec le nombre approprié, et passer la référence de l'objet à chaque thread.

À l'achèvement de sa tâche, chaque fil appelle CountDownLatch.countDown() qui décrète le compteur interne. Le fil principal, après avoir démarré tous les autres, devrait faire l'appel de blocage CountDownLatch.await() . Il sera libérée dès que le compteur interne a atteint 0.

attention, avec cet objet, un InterruptedException peut être jeté.

9
répondu interDist 2016-07-29 17:05:00

Vous ne

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

après cela pour loop, vous pouvez être sûr que tous les threads ont terminé leurs travaux.

8
répondu aioobe 2011-10-29 13:42:41

stocke les Thread-objects dans une collection (comme une liste ou un ensemble), puis boucle à travers la collection une fois que les threads sont commencés et appelle join() sur les Threads.

4
répondu esaj 2016-08-20 09:00:30

vous pouvez utiliser la méthode Threadf#join à cette fin.

2
répondu Narendra Yadala 2016-08-20 08:59:42

bien que non pertinent au problème de L'OP, si vous êtes intéressé par la synchronisation (plus précisément, un rendez-vous) avec exactement un fil, vous pouvez utiliser un échangeur

dans mon cas, j'ai dû mettre en pause le thread parent jusqu'à ce que le thread enfant fasse quelque chose, par exemple compléter son initialisation. Un CountDownLatch fonctionne aussi bien.

2
répondu 18446744073709551615 2016-08-20 09:03:29

un service d'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
répondu 2011-10-29 13:46:41

essayez ça, ça va marcher.

  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
répondu Jeyaraj.J 2015-05-06 13:13:12

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

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

c'est super simple et lisible. Dans les coulisses, il utilise le pool fork join de JVM par défaut, ce qui signifie qu'il attendra que tous les threads soient terminés avant de continuer. Pour mon cas, c'était une solution soignée, parce que c'était le seul parallelStream dans mon application. Si vous avez plus d'un parallelStream fonctionnant simultanément, veuillez lire le lien ci-dessous.

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

1
répondu Madis Pukkonen 2016-06-20 06:00:21

utilisez ceci dans votre thread principal: while (!exécuteur.isTerminated()); Mettez cette ligne de code après avoir démarré tous les threads du service exécuteur. Cela ne démarrera le thread principal qu'une fois tous les threads lancés par les exécuteurs terminés. Appelez l'exécuteur testamentaire.shutdown(); avant de le au-dessus de la boucle.

-1
répondu Maggy 2016-01-07 15:13:37