460 Stimmen

Wie kann man mit ExecutorService warten, bis alle Threads beendet sind?

Ich muss eine gewisse Anzahl von Aufgaben 4 auf einmal ausführen, etwa so:

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
    taskExecutor.execute(new MyTask());
}
//...wait for completion somehow

Wie kann ich benachrichtigt werden, wenn sie alle abgeschlossen sind? Im Moment fällt mir nichts Besseres ein, als einen globalen Aufgabenzähler zu setzen und ihn am Ende jeder Aufgabe zu verringern und dann in einer Endlosschleife zu überwachen, dass dieser Zähler 0 wird; oder eine Liste von Futures zu erhalten und in einer Endlosschleife isDone für alle zu überwachen. Was sind bessere Lösungen, die keine Endlosschleifen beinhalten?

Danke.

517voto

cletus Punkte 596503

Grundsätzlich auf einer ExecutorService Sie rufen shutdown() und dann awaitTermination() :

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
  taskExecutor.execute(new MyTask());
}
taskExecutor.shutdown();
try {
  taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  ...
}

199voto

ChssPly76 Punkte 97241

Verwenden Sie eine CountDownLatch :

CountDownLatch latch = new CountDownLatch(totalNumberOfTasks);
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
  taskExecutor.execute(new MyTask());
}

try {
  latch.await();
} catch (InterruptedException E) {
   // handle
}

und innerhalb Ihrer Aufgabe (eingeschlossen in try / finally)

latch.countDown();

102voto

sjlee Punkte 7446

ExecutorService.invokeAll() tut es für Sie.

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
List<Callable<?>> tasks; // your tasks
// invokeAll() returns when all tasks are complete
List<Future<?>> futures = taskExecutor.invokeAll(tasks);

55voto

rogerdpack Punkte 55995

Sie können auch Listen von Futures verwenden:

List<Future> futures = new ArrayList<Future>();
// now add to it:
futures.add(executorInstance.submit(new Callable<Void>() {
  public Void call() throws IOException {
     // do something
    return null;
  }
}));

dann, wenn Sie auf alle von ihnen beitreten möchten, seine im Wesentlichen das Äquivalent von Beitritt auf jeder, (mit dem zusätzlichen Vorteil, dass es Ausnahmen von Kind-Threads an die Haupt wieder aufruft):

for(Future f: this.futures) { f.get(); }

Grundsätzlich besteht der Trick darin, .get() für jede Zukunft einzeln aufzurufen, anstatt in einer Endlosschleife isDone() für (alle oder jede) aufzurufen. Auf diese Weise ist gewährleistet, dass Sie durch diesen Block "weitergehen", sobald der letzte Thread beendet ist. Der Nachteil ist, dass der .get()-Aufruf erneut Ausnahmen auslöst. Wenn einer der Threads stirbt, würden Sie aus diesem Block möglicherweise aussteigen, bevor die anderen Threads fertig sind [um dies zu vermeiden, könnten Sie eine catch ExecutionException um den Anruf zu erhalten]. Die andere Einschränkung ist es behält einen Verweis auf alle Threads, so dass, wenn sie Thread-lokale Variablen haben sie nicht gesammelt werden, bis nachdem Sie über diesen Block (obwohl Sie in der Lage sein könnte, um dies zu umgehen, wenn es ein Problem wurde, durch Entfernen von Futures aus der ArrayList). Wenn Sie wissen wollen, welcher Future "zuerst fertig ist", könnten Sie etwas wie folgt verwenden https://stackoverflow.com/a/31885029/32453

51voto

AdamSkywalker Punkte 11080

In Java8 können Sie dies mit ErfüllbarZukunft :

ExecutorService es = Executors.newFixedThreadPool(4);
List<Runnable> tasks = getTasks();
CompletableFuture<?>[] futures = tasks.stream()
                               .map(task -> CompletableFuture.runAsync(task, es))
                               .toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures).join();    
es.shutdown();

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X