2009-11-05 8 views
25

Ich habe diese ziemlich einfache Frage über die ThreadPoolExecutor. Ich habe die folgende Situation: Ich muss Objekte aus einer Warteschlange konsumieren, die entsprechenden Worker-Aufgaben für sie erstellen und sie an den ThreadPoolExecutor übergeben. Das ist ziemlich einfach. Aber innerhalb eines Shutdown-Szenarios viele Arbeiter können zur Ausführung in die Warteschlange gestellt werden. Da eine dieser Aufgaben möglicherweise eine Stunde lang ausgeführt wird und ich ein relativ schnelles ordnungsgemäßes Herunterfahren der Anwendung möchte, möchte ich alle in der Warteschlange befindlichen Aufgaben vom ThreadPoolExecutor verwerfen, während die bereits verarbeitenden Aufgaben normal beendet werden sollten.Alle Aufgaben in der Warteschlange eines ThreadPoolExecutor entfernen

Die ThreadPoolExecutor-Dokumentation verfügt über eine remove()-Methode, die es jedoch nur ermöglicht, bestimmte Aufgaben zu entfernen. purge() funktioniert nur für bereits abgebrochene Future-Aufgaben. Meine Idee war, die Warteschlange mit allen Aufgaben in der Warteschlange zu löschen. Der ThreadPoolExecutor ermöglicht den Zugriff auf diese interne Warteschlange aber die Dokumentation wird:

Method getQueue() ermöglicht den Zugang zur Arbeitswarteschlange für die Zwecke der und Debugging-Überwachung. Die Verwendung dieser Methode für jeden anderen Zweck ist stark abgeraten.

So greifen diese Warteschlange und Clearing ist keine Option. Auch dieser Ausschnitt der Dokumentation sagt:

zwei mitgelieferten Methoden, remove (java.lang.Runnable) und Löschen() sind in Speicher Gewinnung zu unterstützen, wenn eine große Anzahl von Tasks in der Liste werden gelöscht .

Wie? Sicher, ich kann eine Liste aller Aufgaben verwalten, die ich an den Executor übergeben habe, und in einem Shutdown-Fall iteriere ich über alle Einträge und entferne sie aus dem ThreadPoolExecutor mit der remove() -Methode ... aber ... komm, das ist a Verschwendung von Speicher und ein Ärger, um diese Liste zu pflegen. (Entfernen von bereits ausgeführten Aufgaben zum Beispiel)

Ich freue mich über Hinweise oder Lösungen!

Antwort

9

Haben Sie überlegt, den ExecutorService zu verpacken? Erstellen Sie einen

, der alle Aufrufe an einen anderen Executor delegiert, aber die Futures in einer eigenen Liste hält. CleanShutdownExecutorService kann dann eine cancelRemainingTasks() -Methode haben, die shutdown() aufruft und dann cancel (false) für alle Futures in ihrer Liste aufruft.

+0

Dies könnte der sauberste Ansatz sein, daher akzeptiere ich diese Antwort. :-) Auch ich habe gehofft, dass da schon sowas ist ... also: lasst uns dieses Ding coden. :-) – Malax

4

Wie ExecutorService.shutdown() nicht genug tut und ExecutorService.shutdownNow() ist zu viel zu tun Ich denke, Sie haben etwas in der Mitte schreiben: Speichern Sie alle Ihre eingereichten Aufgaben und entfernen Sie sie manuell nach (oder vor) shutdown() aufrufen.

+0

"Initiiert ein ordnungsgemäßes Herunterfahren, bei dem zuvor übergebene Aufgaben ausgeführt werden, aber keine neuen Aufgaben akzeptiert werden." Ich habe mehrere Aufgaben eingereicht, die nicht mehr ausgeführt werden sollten. Aber die Aufgaben, die ** ausführen **, sollten nicht enden. – Malax

+0

Sie haben Ihre Antwort bearbeitet, so dass mein Kommentar nicht mehr vollständig gültig ist. Dennoch gibt die Dokumentation shutdownNow() Folgendes an: "Versuche, alle aktiv ausgeführten Aufgaben zu stoppen." Was ich nicht will. :) – Malax

+0

Oh, richtig, ich habe die Hälfte des Satzes überlesen. Nun, ich denke, Ihre einzige Option ist dann, sich an alle übergebenen 'Runnable' zu ​​erinnern und sie manuell zu entfernen. Ich werde entsprechend bearbeiten. – Bombe

1

Bombes Antwort ist genau das, was Sie wollen. shutdownNow() stoppt alles mit der Nuke und pflastern Ansatz. Dies ist das Beste, was Sie tun können, wenn Sie die von Ihnen verwendete Implementierung ThreadPoolExecutor nicht unterklassifizieren.

0

Eine verrückte und unreinen Lösung, die funktionieren könnte (real nicht zu Ende gedacht oder getestet) wäre die interrupt() Ihrer WorkerTasks zu überschreiben, die nur bei einigen globalen Wert gesetzt wird sich weigern, Abschaltung, wenn interrupt() ihnen durch shutdownNow genannt wird ().

Das sollte Ihnen erlauben, shutdownNow() nicht zu verwenden?

0

Sagen Sie Ihrem Thread-Pool, dass er heruntergefahren werden soll, getQueue, und entfernen Sie jedes Runnable mit Hilfe der remove-Methode für jedes Ergebnis in einzelne Runnables. Abhängig vom Typ der Warteschlange können Sie die Löschvorgänge möglicherweise aufgrund von Rückgabewerten vorzeitig anhalten.

Grundsätzlich ist dies die Warteschlange und Clearing, nur Clearing über die Methoden, die funktionieren. Anstatt sich alle Eingaben manuell zu merken, nutzen Sie die Tatsache, dass sich der Thread-Pool bereits an alle Eingaben erinnern muss. Allerdings müssen Sie wahrscheinlich eine defensive Kopie der Warteschlange erstellen, da es sich um eine Live-Ansicht handelt. Daher würde das Entfernen wahrscheinlich zu einer Ausnahme für gleichzeitige Änderungen führen, wenn Sie über die Live-Ansicht iterieren/sich an sie binden.

12

Ich arbeitete an einer App mit langen laufenden Threads. Wir machen dies beim Herunterfahren,

BlockingQueue<Runnable> queue = threadPool.getQueue(); 
List<Runnable> list = new ArrayList<Runnable>(); 
int tasks = queue.drainTo(list); 

Die Liste wird in einer Datei gespeichert. Beim Start wird die Liste wieder zum Pool hinzugefügt, so dass keine Aufträge verloren gehen.

+2

Schöne Ergänzung zu meiner Frage. :-) Mir ist die Persistenz der Aufgaben kein Problem. Aber deine Zugabe kann anderen helfen, danke! :) – Malax

+0

Ich bin mir nicht sicher, ob das der richtige Ansatz ist. Wenn Sie shutdown mit shutdown() beenden, wartet es, bis alles beendet ist (sogar in der Warteschlange befindliche Tasks). Wenn Sie dies mit shutdownNow() tun, gibt diese Methode Ihnen bereits die Liste der in die Warteschlange eingereihten Tasks zurück. Nur für den Fall. – Whimusical

0

Funktioniert nicht nach dem Herunterfahren?

executor.shutdown(); executor.awaitTermination (60, TimeUnit.SECONDS)

1

Sie können versuchen, allowCoreThreadTimeOut(true);

+0

Sie sollten Ihre Antwort entwickeln und klarstellen, dass Sie das OP tatsächlich _beantworten_. –

3

Dies ist eine alte Frage, aber für den Fall, das hilft jemand anderes: Sie einen flüchtigen boolean einstellen könnte, wenn Sie Shutdown() aufrufen, und jede eingereichte Aufgabe muss beendet werden, wenn der Boolesche Wert vor dem tatsächlichen Start gesetzt wird. Dies wird Aufgaben ermöglichen, die tatsächlich begonnen haben, zu vervollständigen, aber verhindern, dass Aufgaben in der Warteschlange ihre eigentliche Aktivität starten.