Ich muss eine bestimmte Anzahl von Aufgaben 4 gleichzeitig ausführen, ungefähr so:
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
taskExecutor.execute(new MyTask());
}
//...wait for completion somehow
Wie kann ich benachrichtigt werden, wenn alle abgeschlossen sind? Im Moment kann ich mir nichts Besseres vorstellen, als einen globalen Aufgabenzähler zu setzen und ihn am Ende jeder Aufgabe zu verringern. Dann diesen Zähler in einer Endlosschleife zu überwachen, um 0 zu werden. oder erhalten Sie eine Liste von Futures und im Endlosschleifenmonitor isDone für alle. Was sind bessere Lösungen ohne Endlosschleifen?
Vielen Dank.
Long.MAX_VALUE, TimeUnit.NANOSECONDS
die einer Zeitüberschreitung entsprechen.java.util.concurrent
Paket - Dokumentation unter demTiming
Abschnitt: „ für immer“ warten, können Sie einen Wert verwenden könnenLong.MAX_VALUE
Verwenden Sie einen CountDownLatch :
und innerhalb Ihrer Aufgabe (in try / finally einschließen)
quelle
ExecutorService.invokeAll()
tut es für dich.quelle
futures
zurückgegeben werden, sind die Aufgaben noch nicht erledigt. Sie werden möglicherweise in Zukunft abgeschlossen und Sie haben einen Link zum Ergebnis. Deshalb heißt es aFuture
. Sie haben die Methode Future.get () , die darauf wartet, dass die Aufgabe abgeschlossen ist, um ein Ergebnis zu erhalten.Sie können auch Futures-Listen verwenden:
Wenn Sie dann allen beitreten möchten, entspricht dies im Wesentlichen dem Beitritt zu jedem (mit dem zusätzlichen Vorteil, dass Ausnahmen von untergeordneten Threads zum Haupt-Thread erneut ausgelöst werden):
Grundsätzlich besteht der Trick darin, .get () für jede Zukunft einzeln aufzurufen, anstatt in einer Endlosschleife isDone () für (alle oder jede) aufzurufen. Sobald der letzte Thread beendet ist, können Sie diesen Block garantiert durchlaufen und passieren. Die Einschränkung besteht darin, dass Sie, da der Aufruf von .get () erneut Ausnahmen auslöst, wenn einer der Threads stirbt, dies möglicherweise auslösen würden, bevor die anderen Threads vollständig abgeschlossen sind [um dies zu vermeiden, können Sie ein https: // hinzufügen stackoverflow.com/a/31885029/32453
catch ExecutionException
Aufruf um den get-Aufruf ]. Die andere Einschränkung ist, dass ein Verweis auf alle Threads beibehalten wird. Wenn sie also lokale Thread-Variablen haben, werden sie erst erfasst, nachdem Sie diesen Block überschritten haben (obwohl Sie dies möglicherweise umgehen können, wenn es zu einem Problem wurde, indem Sie es entfernen Zukunft ist aus der ArrayList). Wenn Sie wissen möchten, welche Zukunft "zuerst endet"quelle
ExecutorCompletionService.take
: stackoverflow.com/a/11872604/199364In Java8 können Sie dies mit CompletableFuture tun :
quelle
ExecutorService es = Executors.newFixedThreadPool(4); List< Future<?>> futures = new ArrayList<>(); for(Runnable task : taskList) { futures.add(es.submit(task)); } for(Future<?> future : futures) { try { future.get(); }catch(Exception e){ // do logging and nothing else } }
Nur meine zwei Cent. Um die Anforderung zu überwinden
CountDownLatch
, die Anzahl der Aufgaben im Voraus zu kennen, können Sie dies auf die altmodische Weise mithilfe einer einfachen Aufgabe tunSemaphore
.Rufen
s.release()
Sie bei Ihrer Aufgabe einfach so an, wie Sie es möchtenlatch.countDown();
quelle
release
Anrufe vor demacquire
Anruf stattfinden, aber nachdem ich die Semaphore-Dokumentation gelesen habe, sehe ich, dass das in Ordnung ist.Ein bisschen spät zum Spiel, aber der Vollendung halber ...
Anstatt darauf zu warten, dass alle Aufgaben erledigt sind, können Sie nach dem Hollywood-Prinzip denken: "Rufen Sie mich nicht an, ich rufe Sie an" - wenn ich fertig bin. Ich denke, der resultierende Code ist eleganter ...
Guave bietet einige interessante Werkzeuge, um dies zu erreichen.
Ein Beispiel ::
Wickeln Sie einen ExecutorService in einen ListeningExecutorService ::
Senden Sie eine Sammlung von Callables zur Ausführung ::
Nun zum wesentlichen Teil:
Fügen Sie der ListenableFuture einen Rückruf hinzu, mit dem Sie benachrichtigt werden können, wenn alle Futures abgeschlossen sind:
Dies bietet auch den Vorteil, dass Sie alle Ergebnisse nach Abschluss der Verarbeitung an einem Ort sammeln können ...
Weitere Informationen hier
quelle
runOnUiThread()
inonSuccess()
.Die CyclicBarrier- Klasse in Java 5 und höher ist für diese Art von Dingen konzipiert.
quelle
Folgen Sie einem der folgenden Ansätze.
submit
zurückExecutorService
und überprüfen Sie den Status mit blockierendem Aufrufget()
desFuture
Objekts, wie von vorgeschlagenKiran
invokeAll()
auf ExecutorServiceshutdown, awaitTermination, shutdownNow
APIs von ThreadPoolExecutor in der richtigen ReihenfolgeVerwandte SE-Fragen:
Wie wird CountDownLatch in Java Multithreading verwendet?
So beenden Sie Java ExecutorService ordnungsgemäß
quelle
Hier sind zwei Optionen, nur ein bisschen verwirren, welche am besten ist.
Option 1:
Option 2:
Hier setzen future.get (); in try catch ist eine gute idee oder?
quelle
Sie können Ihre Aufgaben in eine andere ausführbare Datei einbinden, die Benachrichtigungen sendet:
quelle
completed
Zähler erhöhen . Nachdem Sie alle gestartet haben, können Sie bei jeder Benachrichtigung feststellen, ob alle Aufgaben abgeschlossen sind. Beachten Sie, dass die Verwendung unbedingt erforderlich ist,try/finally
damit eine fertige Benachrichtigung (oder eine alternative Benachrichtigung imcatch
Block) auch dann angezeigt wird, wenn eine Aufgabe fehlschlägt. Sonst würde ich ewig warten.Ich habe gerade ein Beispielprogramm geschrieben, das Ihr Problem löst. Es wurde keine präzise Implementierung angegeben, daher werde ich eine hinzufügen. Obwohl Sie
executor.shutdown()
und verwenden könnenexecutor.awaitTermination()
, ist dies nicht die beste Vorgehensweise, da die von verschiedenen Threads benötigte Zeit nicht vorhersehbar wäre.quelle
Nur um hier mehr Alternativen zur Verwendung von Riegeln / Barrieren anzubieten. Sie können die Teilergebnisse auch abrufen, bis alle mit CompletionService abgeschlossen sind .
Von Java Concurrency in der Praxis: "Wenn Sie eine Reihe von Berechnungen an einen Executor senden müssen und deren Ergebnisse abrufen möchten, sobald sie verfügbar sind, können Sie die mit jeder Aufgabe verknüpfte Zukunft beibehalten und wiederholt nach Abschluss abrufen, indem Sie get with a aufrufen Timeout von Null. Dies ist möglich, aber langwierig . Glücklicherweise gibt es einen besseren Weg : einen Abschlussservice. "
Hier die Umsetzung
quelle
Dies ist meine Lösung, basierend auf dem Tipp "AdamSkywalker", und sie funktioniert
quelle
Sie könnten diesen Code verwenden:
quelle
Ich habe das folgende Arbeitsbeispiel erstellt. Die Idee ist, eine Möglichkeit zu haben, einen Aufgabenpool (ich verwende eine Warteschlange als Beispiel) mit vielen Threads (programmgesteuert durch die Anzahl der Aufgaben / Schwellenwert bestimmt) zu verarbeiten und zu warten, bis alle Threads abgeschlossen sind, um mit einer anderen Verarbeitung fortzufahren.
Ich hoffe es hilft!
quelle
Sie können Ihre eigene Unterklasse von ExecutorCompletionService zum Umschließen
taskExecutor
und Ihre eigene Implementierung von BlockingQueue verwenden , um informiert zu werden, wenn jede Aufgabe abgeschlossen ist, und einen beliebigen Rückruf oder eine andere Aktion ausführen, wenn die Anzahl der abgeschlossenen Aufgaben Ihr gewünschtes Ziel erreicht.quelle
Sie sollten verwenden
executorService.shutdown()
undexecutorService.awaitTermination
Methode.Ein Beispiel wie folgt:
quelle
Also poste ich meine Antwort von der verknüpften Frage hier, falls jemand einen einfacheren Weg möchte, dies zu tun
quelle
Java 8 - Wir können die Stream-API verwenden, um Streams zu verarbeiten. Bitte sehen Sie den Ausschnitt unten
quelle
Wenn Sie
doSomething()
einige andere Ausnahmen auslösen, wird derlatch.countDown()
Schein nicht ausgeführt. Was soll ich also tun?quelle
Wenn Sie mehr Thread ExecutionServices SEQUENTIALLY verwenden und warten möchten, bis JEDER EXECUTIONSERVICE abgeschlossen ist. Der beste Weg ist wie unten;
quelle
Dies könnte helfen
quelle
Sie können waitTillDone () für diese Runner- Klasse aufrufen :
Sie können diese Klasse wiederverwenden und waitTillDone () so oft aufrufen, wie Sie möchten, bevor Sie shutdown () aufrufen . Außerdem ist Ihr Code äußerst einfach . Auch Sie müssen nicht wissen , die Anzahl der Aufgaben im Voraus.
Um es zu benutzen, füge einfach diesen Gradle / Maven hinzu
compile 'com.github.matejtymes:javafixes:1.3.1'
Abhängigkeit zu Ihrem Projekt hinzu.Weitere Details finden Sie hier:
https://github.com/MatejTymes/JavaFixes
quelle
In Executor gibt es eine Methode
getActiveCount()
, die die Anzahl der aktiven Threads angibt.Nachdem wir den Thread überspannt haben, können wir überprüfen, ob der
activeCount()
Wert ist0
. Sobald der Wert Null ist, werden derzeit keine aktiven Threads ausgeführt, was bedeutet, dass die Aufgabe abgeschlossen ist:quelle