web-dev-qa-db-fra.com

Attendre que plusieurs threads se terminent en Java

Au cours de l'exécution de mon programme, un certain nombre de threads sont démarrés. La quantité de threads varie en fonction des paramètres définis par l'utilisateur, mais ils exécutent tous la même méthode avec différentes variables.

Dans certaines situations, un nettoyage est requis en cours d'exécution, une partie de cela arrête tous les threads, je ne veux pas qu'ils s'arrêtent immédiatement cependant, je viens de définir une variable qu'ils vérifient pour les terminer. Le problème est qu'il peut s'écouler jusqu'à 1/2 seconde avant l'arrêt du fil. Cependant, je dois m'assurer que tous les threads se sont arrêtés avant que le nettoyage puisse continuer. Le nettoyage est exécuté à partir d'un autre thread, donc techniquement j'ai besoin de ce thread pour attendre la fin des autres threads.

J'ai pensé à plusieurs façons de procéder, mais elles semblent toutes trop complexes. J'espérais qu'il y aurait une méthode qui pourrait attendre qu'un groupe de threads se termine. Existe-t-il quelque chose comme ça?

Merci.

35
A Jackson

Rejoignez-les un par un:

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

(Vous devrez faire quelque chose avec InterruptedException, et vous voudrez peut-être prévoir un délai d'attente en cas de problème, mais c'est l'idée de base ...)

58
Jon Skeet

Si vous utilisez Java 1.5 ou supérieur, vous pouvez essayer CyclicBarrier . Vous pouvez passer l'opération de nettoyage comme paramètre constructeur et appeler simplement barrier.await() sur tous les threads en cas de besoin de nettoyage.

14
Tadeusz Kopec

Avez-vous vu les classes Executor dans Java.util.concurrent? Vous pouvez exécuter vos threads via un ExecutorService. Il vous donne un seul objet que vous pouvez utiliser pour annuler les threads ou attendre qu'ils se terminent.

9
Kenster

Définissez vous-même une ou plusieurs méthodes utilitaires:

public static waitFor(Collection<? extends Thread) c) throws InterruptedException {
    for(Thread t : c) t.join();
}

Ou vous pouvez avoir un tableau

public static waitFor(Thread[] ts) throws InterruptedException {
    waitFor(Arrays.asList(ts));
}

Vous pouvez également envisager d'utiliser un CyclicBarrier dans le Java.util.concurrent bibliothèque pour implémenter un point arbitraire rendez-vous entre plusieurs threads.

8
oxbow_lakes

Si vous contrôlez la création des Threads (soumission à un ExecutorService), il semble que vous pouvez utiliser un ExecutorCompletionService voir ExecutorCompletionService? Pourquoi en avez-vous besoin si nous avons invokeAll? pour diverses réponses là-bas .

Si vous ne contrôlez pas la création de threads, voici une approche qui vous permet de joindre les threads "un par un à la fin" (et de savoir lequel se termine en premier, etc.), inspiré du Ruby ThreadWait class. Fondamentalement, en renouvelant les "threads d'observation" qui alertent lorsque les autres threads se terminent, vous pouvez savoir quand le thread "suivant" sur de nombreux se termine.

Vous l'utiliseriez quelque chose comme ceci:

JoinThreads join = new JoinThreads(threads);
for(int i = 0; i < threads.size(); i++) {
  Thread justJoined = join.joinNextThread();
  System.out.println("Done with a thread, just joined=" + justJoined);
}

Et la source:

public static class JoinThreads {
  Java.util.concurrent.LinkedBlockingQueue<Thread> doneThreads = 
      new LinkedBlockingQueue<Thread>();

  public JoinThreads(List<Thread> threads) {
    for(Thread t : threads) {
      final Thread joinThis = t;
      new Thread(new Runnable() {
        @Override
        public void run() {
          try {
            joinThis.join();
            doneThreads.add(joinThis);
          }
          catch (InterruptedException e) {
            // "should" never get here, since we control this thread and don't call interrupt on it
          }
        }
      }).start();
    }

  }

  Thread joinNextThread() throws InterruptedException {
    return doneThreads.take();
  }
}

La bonne partie de cela est qu'il fonctionne avec des threads génériques Java, sans modification, n'importe quel thread peut être joint. La mise en garde est qu'il nécessite une création de thread supplémentaire. De plus, cette implémentation particulière "laisse les threads derrière "si vous n'appelez pas joinNextThread () le nombre total de fois, et n'a pas de méthode" close ", etc. Commentez ici si vous souhaitez créer une version plus soignée. Vous pouvez également utiliser ce même type de modèle avec "Futures" au lieu d'objets Thread, etc.

1
rogerdpack