Was ist der einfachste Weg, um zu warten, bis alle Aufgaben erledigt sind ExecutorService
? Meine Aufgabe ist in erster Linie rechnerisch, daher möchte ich nur eine große Anzahl von Jobs ausführen - einen auf jedem Kern. Im Moment sieht mein Setup so aus:
ExecutorService es = Executors.newFixedThreadPool(2);
for (DataTable singleTable : uniquePhrases) {
es.execute(new ComputeDTask(singleTable));
}
try{
es.wait();
}
catch (InterruptedException e){
e.printStackTrace();
}
ComputeDTask
implementiert lauffähig. Dies scheint die Aufgaben korrekt auszuführen, aber der Code stürzt wait()
mit ab IllegalMonitorStateException
. Das ist seltsam, weil ich mit einigen Spielzeugbeispielen herumgespielt habe und es schien zu funktionieren.
uniquePhrases
enthält mehrere Zehntausende von Elementen. Sollte ich eine andere Methode verwenden? Ich suche etwas so Einfaches wie möglich
java
multithreading
threadpool
executorservice
George Smiley
quelle
quelle
es
), wenn Sie darauf warten möchten - die Sperre wird während des Wartens automatisch aufgehobenExecutors.newFixedThreadPool(System.getRuntime().availableProcessors());
Antworten:
Der einfachste Ansatz besteht darin, zu verwenden
ExecutorService.invokeAll()
, was in einem Einzeiler das tut, was Sie wollen. In Ihrer Sprache müssen SieComputeDTask
die Implementierung ändern oder umbrechenCallable<>
, was Ihnen einiges an Flexibilität geben kann. Wahrscheinlich gibt es in Ihrer App eine sinnvolle Implementierung vonCallable.call()
, aber hier ist eine Möglichkeit, sie zu verpacken, wenn Sie sie nicht verwendenExecutors.callable()
.Wie andere bereits betont haben, können Sie
invokeAll()
gegebenenfalls die Timeout-Version von verwenden . In diesem Beispielanswers
wird es eine Reihe vonFuture
s enthalten, die Nullen zurückgeben (siehe Definition vonExecutors.callable()
. Wahrscheinlich möchten Sie ein leichtes Refactoring durchführen, damit Sie eine nützliche Antwort oder einen Verweis auf den zugrunde liegenden Wert erhaltenComputeDTask
, aber ich kann Erzählen Sie nicht von Ihrem Beispiel.Wenn es nicht klar ist, beachten Sie, dass
invokeAll()
es nicht zurückkehrt, bis alle Aufgaben abgeschlossen sind. (Das heißt, alleFuture
s in Ihreranswers
Sammlung werden gemeldet,.isDone()
wenn Sie dazu aufgefordert werden.) Dies vermeidet das manuelle Herunterfahren, Warten auf Beendigung usw. und ermöglicht es Ihnen, dies beiExecutorService
Bedarf für mehrere Zyklen sauber wiederzuverwenden .Es gibt einige verwandte Fragen zu SO:
So warten Sie, bis alle Threads abgeschlossen sind
Rückgabewerte von Java-Threads
invokeAll () ist nicht bereit, eine Sammlung <Callable <t>> zu akzeptieren
Muss ich synchronisieren?
Keines davon ist genau auf Ihre Frage zugeschnitten, aber es liefert ein wenig Farbe darüber, wie Leute denken
Executor
/ verwendetExecutorService
werden sollten.quelle
Wenn Sie warten möchten, bis alle Aufgaben abgeschlossen sind, verwenden Sie
shutdown
stattdessen die Methodewait
. Dann folgen Sie ihm mitawaitTermination
.Außerdem können Sie
Runtime.availableProcessors
die Anzahl der Hardware-Threads abrufen, um Ihren Threadpool ordnungsgemäß zu initialisieren.quelle
awaitTermination
erfordert eine Timeout-Zeit als Parameter. Während es möglich ist, eine endliche Zeit anzugeben und eine Schleife darum zu legen, um zu warten, bis alle Threads fertig sind, habe ich mich gefragt, ob es eine elegantere Lösung gibt.Wenn das Warten auf das
ExecutorService
Beenden aller Aufgaben im Ziel nicht genau Ihr Ziel ist, sondern das Warten, bis ein bestimmter Stapel von Aufgaben abgeschlossen ist, können Sie einCompletionService
- speziell einExecutorCompletionService
.Die Idee ist, eine
ExecutorCompletionService
Verpackung zu erstellenExecutor
, einreichen einige bekannte Anzahl von Aufgaben durch dieCompletionService
, dann ist das Zeichnen derselben Anzahl von Ergebnissen aus dem Abschluss - Warteschlange unter Verwendung von entwedertake()
(die Blöcke) oderpoll()
(was nicht). Sobald Sie alle erwarteten Ergebnisse für die von Ihnen eingereichten Aufgaben gezeichnet haben, wissen Sie, dass alle erledigt sind.Lassen Sie mich dies noch einmal sagen, da es aus der Benutzeroberfläche nicht ersichtlich ist: Sie müssen wissen, wie viele Dinge Sie in das
CompletionService
eingeben, um zu wissen, wie viele Dinge Sie herausziehen möchten. Dies ist besonders bei dertake()
Methode von Bedeutung: Rufen Sie sie einmal zu oft auf, und Ihr aufrufender Thread wird blockiert, bis ein anderer Thread einen anderen Job an denselben sendetCompletionService
.Es gibt einige Beispiele, die zeigen, wie
CompletionService
das Buch Java Concurrency in der Praxis verwendet wird .quelle
CompletionService
Wenn Sie warten möchten, bis der Executor-Service die Ausführung beendet hat, rufen Sie an und warten Sie
shutdown()
dann auf Terminierung (Einheiten, Einheitentyp) , zawaitTermination(1, MINUTE)
. Der ExecutorService blockiert nicht auf seinem eigenen Monitor, sodass Siewait
usw. nicht verwenden können .quelle
awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
stackoverflow.com/a/1250655/32453Sie können warten, bis die Jobs in einem bestimmten Intervall beendet sind:
Oder Sie könnten ExecutorService verwenden . einreichen ( Runnable ) und sammeln Sie die Zukunft Objekte , die es gibt und Call get () auf jedem wiederum zu warten , bis sie zu beenden.
InterruptedException ist äußerst wichtig, um richtig zu handeln. Auf diese Weise können Sie oder die Benutzer Ihrer Bibliothek einen langen Prozess sicher beenden.
quelle
Benutz einfach
In jedem Thread
und als Barriere
quelle
Grundursache für die IllegalMonitorStateException :
Von Ihrem Code aus haben Sie gerade wait () für ExecutorService aufgerufen, ohne die Sperre zu besitzen.
Der folgende Code wird behoben
IllegalMonitorStateException
Befolgen Sie einen der folgenden Ansätze, um auf die Fertigstellung aller Aufgaben zu warten, die an gesendet wurden
ExecutorService
.Durchlaufen Sie alle
Future
Aufgaben von Anfangsubmit
anExecutorService
und überprüfen Sie den Status, indem Sie den Aufrufget()
desFuture
Objekts blockierenVerwenden von invokeAll on
ExecutorService
Mit CountDownLatch
Verwenden von ForkJoinPool oder newWorkStealingPool von
Executors
(seit Java 8)Shutdown der Pool wie in der Oracle - Dokumentation empfohlen Seite
Wenn Sie ordnungsgemäß auf den Abschluss aller Aufgaben warten möchten, wenn Sie Option 5 anstelle der Optionen 1 bis 4 verwenden, ändern Sie diese
zu
a,
while(condition)
die alle 1 Minute überprüft.quelle
Sie können die
ExecutorService.invokeAll
Methode verwenden. Sie führt alle Aufgaben aus und wartet, bis alle Threads ihre Aufgabe beendet haben.Hier ist komplettes Javadoc
Sie können auch eine überladene Version dieser Methode verwenden, um das Zeitlimit anzugeben.
Hier ist Beispielcode mit
ExecutorService.invokeAll
quelle
Ich habe auch die Situation, dass ich eine Reihe von Dokumenten zum Crawlen habe. Ich beginne mit einem anfänglichen "Seed" -Dokument, das verarbeitet werden soll. Dieses Dokument enthält Links zu anderen Dokumenten, die ebenfalls verarbeitet werden sollen, und so weiter.
In meinem Hauptprogramm möchte ich nur Folgendes schreiben, in dem
Crawler
eine Reihe von Threads gesteuert werden.Die gleiche Situation würde passieren, wenn ich durch einen Baum navigieren wollte; Ich würde den Stammknoten einfügen, der Prozessor für jeden Knoten würde der Warteschlange nach Bedarf untergeordnete Elemente hinzufügen, und eine Reihe von Threads würde alle Knoten im Baum verarbeiten, bis keine mehr vorhanden wären.
Ich konnte in der JVM nichts finden, was ich etwas überraschend fand. Also habe ich eine Klasse geschrieben,
ThreadPool
die man entweder direkt oder in einer Unterklasse verwenden kann, um für die Domäne geeignete Methoden hinzuzufügen, zschedule(Document)
. Ich hoffe es hilft!ThreadPool Javadoc | Maven
quelle
Fügen Sie alle Threads in der Sammlung hinzu und senden Sie sie mit
invokeAll
. Wenn Sie dieinvokeAll
Methode von verwenden könnenExecutorService
, fährt JVM erst mit der nächsten Zeile fort, wenn alle Threads abgeschlossen sind.Hier gibt es ein gutes Beispiel: invokeAll via ExecutorService
quelle
Senden Sie Ihre Aufgaben an den Runner und warten Sie, bis Sie die Methode waitTillDone () wie folgt aufrufen :
Um es zu verwenden, fügen Sie diese Gradle / Maven-Abhängigkeit hinzu:
'com.github.matejtymes:javafixes:1.0'
Weitere Informationen finden Sie hier: https://github.com/MatejTymes/JavaFixes oder hier: http://matejtymes.blogspot.com/2016/04/executor-that-notifications-you-when-task.html
quelle
Eine einfache Alternative dazu ist die Verwendung von Threads zusammen mit Join. Siehe: Threads verbinden
quelle
Ich werde nur warten, bis der Executor mit einem festgelegten Timeout beendet ist, das Ihrer Meinung nach für die Ausführung der Aufgaben geeignet ist.
quelle
Klingt so, als ob Sie
ForkJoinPool
den globalen Pool benötigen und verwenden, um Aufgaben auszuführen.Das Schöne ist,
pool.awaitQuiescence
dass die Methode den Thread des Aufrufers blockiert, um seine Aufgaben auszuführen, und dann zurückkehrt, wenn er wirklich leer ist.quelle